29 errors.push_back(
"Profile name cannot be empty");
33 errors.push_back(
"Profile version cannot be empty");
37 std::vector<String> config_errors;
38 if (!
config_.Validate(config_errors)) {
39 errors.insert(errors.end(), config_errors.begin(), config_errors.end());
42 return errors.empty();
48 json[
"profile_name"] =
name_;
54 json[
"config"] =
config_.ToJson();
62 if (!json.contains(
"profile_name")) {
63 throw std::runtime_error(
"Missing required field: profile_name");
66 if (!json.contains(
"config")) {
67 throw std::runtime_error(
"Missing required field: config");
71 const auto &config_json = json[
"config"];
72 if (config_json.empty()) {
73 throw std::runtime_error(
"Config object cannot be empty");
77 if (!config_json.contains(
"document") || !config_json.contains(
"tree") || !config_json.contains(
"node")) {
78 throw std::runtime_error(
"Config must contain minimum required sections: document, tree, and node");
86 if (json.contains(
"description")) {
89 if (json.contains(
"version")) {
92 if (json.contains(
"author")) {
95 if (json.contains(
"created_timestamp")) {
98 if (json.contains(
"modified_timestamp")) {
101 }
catch (
const std::exception &e) {
102 LOG_ERROR(
"ParserProfile",
"Error parsing JSON profile: " + std::string(e.what()));
109 nlohmann::json json =
ToJson();
110 std::ofstream file(filepath);
112 if (!file.is_open()) {
113 LOG_ERROR(
"ParserProfile",
"Failed to open file for writing: " + filepath);
118 file << std::setw(4) << json << std::endl;
121 LOG_INFO(
"ParserProfile",
"Saved profile '" +
name_ +
"' to: " + filepath);
123 }
catch (
const std::exception &e) {
124 LOG_ERROR(
"ParserProfile",
"Error saving profile to file: " + std::string(e.what()));
131 std::ifstream file(filepath);
133 if (!file.is_open()) {
134 LOG_ERROR(
"ParserProfile",
"Failed to open file for reading: " + filepath);
144 LOG_INFO(
"ParserProfile",
"Loaded profile '" +
name_ +
"' from: " + filepath);
146 }
catch (
const std::exception &e) {
147 LOG_ERROR(
"ParserProfile",
"Error loading profile from file: " + std::string(e.what()));
155 String cloned_name = new_name.empty() ?
name_ : new_name;
156 auto cloned = std::make_shared<ParserProfile>(cloned_name,
description_);
167 auto profile = std::make_shared<ParserProfile>(
"Default",
"Default BehaviorTree.CPP format");
168 profile->SetVersion(
"1.0");
169 profile->SetAuthor(
"EmberForge");
173 profile->SetConfig(config);
180 std::make_shared<ParserProfile>(
"Generic",
"Generic permissive format for various BT implementations");
181 profile->SetVersion(
"1.0");
182 profile->SetAuthor(
"EmberForge");
185 profile->SetConfig(config);
191 auto now = std::chrono::system_clock::now();
192 auto duration = now.time_since_epoch();
193 return std::chrono::duration_cast<std::chrono::seconds>(duration).count();
#define LOG_ERROR(category, message)
#define LOG_INFO(category, message)
Configuration for XML parser behavior and element/attribute mappings.
static ParserConfig CreateDefault()
static ParserConfig CreateGeneric()
static std::shared_ptr< ParserProfile > CreateGenericProfile()
static int64_t GetCurrentTimestamp()
nlohmann::json ToJson() const
static std::shared_ptr< ParserProfile > CreateDefaultProfile()
bool LoadFromFile(const String &filepath)
int64_t modified_timestamp_
bool Validate(std::vector< String > &errors) const
void SetConfig(const ParserConfig &config)
void FromJson(const nlohmann::json &json)
std::shared_ptr< ParserProfile > Clone(const String &new_name="") const
int64_t created_timestamp_
void UpdateModifiedTimestamp()
bool SaveToFile(const String &filepath) const
Main types header for EmberCore.
std::string String
Framework-agnostic string type.