6#include <nlohmann/json.hpp>
17 return stat(path.c_str(), &st) == 0 && S_ISDIR(st.st_mode);
35 const char *env_resources = std::getenv(
"EMBER_RESOURCES");
37 resources_base =
String(env_resources);
41 ssize_t len = readlink(
"/proc/self/exe", exe_path,
sizeof(exe_path) - 1);
46 size_t last_slash = exe_dir.find_last_of(
'/');
47 if (last_slash != String::npos) {
48 exe_dir = exe_dir.substr(0, last_slash);
52 std::vector<String> candidates = {
53 exe_dir +
"/resources",
54 exe_dir +
"/../resources",
55 exe_dir +
"/../../resources",
58 for (
const auto &candidate : candidates) {
60 resources_base = candidate;
68 if (!resources_base.empty()) {
70 config_file_ = resources_base +
"/parser_profiles/config.json";
75 LOG_WARNING(
"ConfigManager",
"Could not find resources directory, using relative path");
101 if (stat(emberforge_dir.c_str(), &st) != 0) {
102 if (mkdir(emberforge_dir.c_str(), 0755) != 0) {
103 LOG_ERROR(
"ConfigManager",
"Failed to create .emberforge directory: " + emberforge_dir);
121 std::lock_guard<std::mutex> lock(
mutex_);
127 LOG_INFO(
"ConfigManager",
"Initializing default profiles");
141 std::lock_guard<std::mutex> lock(
mutex_);
147 LOG_ERROR(
"ConfigManager",
"Failed to open profiles directory");
151 int loaded_count = 0;
152 struct dirent *entry;
153 while ((entry = readdir(dir)) !=
nullptr) {
154 String filename = entry->d_name;
157 if (filename ==
"config.json") {
162 if (filename.length() > 5 && filename.substr(filename.length() - 5) ==
".json") {
175 LOG_INFO(
"ConfigManager",
"Loaded " + std::to_string(loaded_count) +
" profile(s)");
183 return std::make_shared<ParserProfile>(profile);
185 }
catch (
const std::exception &e) {
186 LOG_ERROR(
"ConfigManager",
"Failed to load profile from " + filepath +
": " + e.what());
192 std::lock_guard<std::mutex> lock(
mutex_);
202 LOG_INFO(
"ConfigManager",
"Saving " + std::to_string(
profiles_.size()) +
" profile(s)");
204 bool all_saved =
true;
206 const String &profile_name = pair.first;
221 LOG_ERROR(
"ConfigManager",
"Profile not found: " + profile_name);
226 return profile->SaveToFile(filepath);
231 LOG_ERROR(
"ConfigManager",
"Cannot add null profile");
235 std::lock_guard<std::mutex> lock(
mutex_);
237 String profile_name = profile->GetName();
239 LOG_ERROR(
"ConfigManager",
"Profile already exists: " + profile_name);
246 LOG_INFO(
"ConfigManager",
"Added profile: " + profile_name);
251 std::lock_guard<std::mutex> lock(
mutex_);
255 LOG_ERROR(
"ConfigManager",
"Cannot remove default profile: " + profile_name);
260 LOG_ERROR(
"ConfigManager",
"Profile not found: " + profile_name);
274 if (unlink(filepath.c_str()) != 0) {
275 LOG_WARNING(
"ConfigManager",
"Failed to delete profile file: " + filepath);
278 LOG_INFO(
"ConfigManager",
"Removed profile: " + profile_name);
283 std::lock_guard<std::mutex> lock(
mutex_);
287 LOG_ERROR(
"ConfigManager",
"Cannot rename default profile: " + old_name);
293 LOG_ERROR(
"ConfigManager",
"Profile not found: " + old_name);
298 LOG_ERROR(
"ConfigManager",
"Profile with new name already exists: " + new_name);
303 profile->SetName(new_name);
304 profile->UpdateModifiedTimestamp();
317 unlink(old_filepath.c_str());
320 LOG_INFO(
"ConfigManager",
"Renamed profile: " + old_name +
" -> " + new_name);
337 std::lock_guard<std::mutex> lock(
mutex_);
344 std::lock_guard<std::mutex> lock(
mutex_);
346 std::vector<String> names;
350 names.push_back(pair.first);
357 std::lock_guard<std::mutex> lock(
mutex_);
362 std::lock_guard<std::mutex> lock(
mutex_);
365 LOG_ERROR(
"ConfigManager",
"Cannot set active profile, profile not found: " + profile_name);
374 LOG_INFO(
"ConfigManager",
"Active profile set to: " + profile_name);
379 std::lock_guard<std::mutex> lock(
mutex_);
383 LOG_ERROR(
"ConfigManager",
"Profile not found for export: " + profile_name);
387 return profile->SaveToFile(filepath);
393 LOG_ERROR(
"ConfigManager",
"Failed to import profile from: " + filepath);
399 String original_name = profile_name;
403 profile_name = original_name +
" (" + std::to_string(counter++) +
")";
406 if (profile_name != original_name) {
407 temp_profile.
SetName(profile_name);
408 LOG_INFO(
"ConfigManager",
"Renamed imported profile to avoid conflict: " + profile_name);
411 auto profile = std::make_shared<ParserProfile>(temp_profile);
424 std::lock_guard<std::mutex> lock(
mutex_);
426 LOG_INFO(
"ConfigManager",
"Resetting to default profiles");
435 String safe_name = profile_name;
436 for (
char &c : safe_name) {
437 if (!isalnum(c) && c !=
'_' && c !=
'-') {
448 if (!file.is_open()) {
458 if (json.contains(
"active_profile")) {
459 String loaded_profile = json[
"active_profile"].get<
String>();
467 "Config file references non-existent profile '" + loaded_profile +
"', using default");
473 }
catch (
const std::exception &e) {
474 LOG_ERROR(
"ConfigManager",
"Failed to load config file: " +
String(e.what()));
487 json[
"version"] =
"1.0";
495 if (!file.is_open()) {
500 file << std::setw(4) << json << std::endl;
505 }
catch (
const std::exception &e) {
506 LOG_ERROR(
"ConfigManager",
"Failed to save config file: " +
String(e.what()));
#define LOG_ERROR(category, message)
#define LOG_WARNING(category, message)
#define LOG_INFO(category, message)
void InitializeDefaultProfilesInternal()
bool EnsureProfilesDirectoryExists()
String GetProfileFilePath(const String &profile_name) const
std::shared_ptr< ParserProfile > GetActiveProfile() const
std::shared_ptr< ParserProfile > LoadProfileFromFile(const String &filepath)
void InitializeDefaultProfiles()
String active_profile_name_
bool SaveProfile(const String &profile_name)
bool SetActiveProfile(const String &profile_name)
static const String GENERIC_PROFILE_NAME
std::vector< String > GetProfileNames() const
std::shared_ptr< ParserProfile > GetProfileInternal(const String &profile_name) const
bool RemoveProfile(const String &profile_name)
bool ImportProfile(const String &filepath, bool make_active=false)
String profiles_directory_
std::shared_ptr< ParserProfile > GetProfile(const String &profile_name) const
bool SaveProfilesInternal()
bool RenameProfile(const String &old_name, const String &new_name)
bool HasProfileInternal(const String &profile_name) const
bool AddProfile(std::shared_ptr< ParserProfile > profile)
static const String DEFAULT_PROFILE_NAME
std::map< String, std::shared_ptr< ParserProfile > > profiles_
bool HasProfile(const String &profile_name) const
ConfigManager(const ConfigManager &)=delete
bool ExportProfile(const String &profile_name, const String &filepath)
static ConfigManager & GetInstance()
A named parser configuration profile with metadata.
static std::shared_ptr< ParserProfile > CreateGenericProfile()
void SetName(const String &name)
static std::shared_ptr< ParserProfile > CreateDefaultProfile()
bool LoadFromFile(const String &filepath)
Main types header for EmberCore.
static bool DirectoryExists(const String &path)
std::string String
Framework-agnostic string type.