diff --git a/include/hyprlang.hpp b/include/hyprlang.hpp index f4705ce..ca2b239 100644 --- a/include/hyprlang.hpp +++ b/include/hyprlang.hpp @@ -389,6 +389,18 @@ namespace Hyprlang { */ CConfigValue* getSpecialConfigValuePtr(const char* category, const char* name, const char* key = nullptr); + /*! + Get a basic or special category's config value ptr. + + Syntax: + - `name` -> `general:gaps_out` + - `category[key]:name` -> `windowrule[name]:enable` + + \note Prefer `getConfigValuePtr` or `getSpecialConfigValuePtr` to avoid unnecessary lookup and parsing. + \warning The pointer is temporary and only valid for a brief moment + */ + CConfigValue* getAnyConfigValuePtr(const char* name); + /*! Get a config value's stored value. Empty on fail */ @@ -409,6 +421,22 @@ namespace Hyprlang { return val->getValue(); } + /*! + Get a basic or special config value's stored value. Empty on fail. + + Syntax: + - `name` -> `general:gaps_out` + - `category[key]:name` -> `windowrule[name]:enable` + + \note Prefer `getConfigValue` or `getSpecialConfigValue` to avoid unnecessary lookup and parsing. + */ + std::any getAnyConfigValue(const char* name) { + CConfigValue* val = getAnyConfigValuePtr(name); + if (!val) + return {}; + return val->getValue(); + } + /*! Check whether a special category with the provided key value exists diff --git a/src/config.cpp b/src/config.cpp index 434c6be..6c95093 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -41,6 +41,20 @@ static size_t seekABIStructSize(const void* begin, size_t startOffset, size_t ma return 0; } +static SParsedConfigName parseConfigName(const char* name) { + const std::string NAME = name; + const auto L = NAME.find('['); + const auto R = NAME.find("]:", L); + + if (L != std::string::npos && R != std::string::npos) + return SParsedConfigName{ + .category = NAME.substr(0, L), + .key = NAME.substr(L + 1, R - L - 1), + .name = NAME.substr(R + 2), + }; + return SParsedConfigName{.name = name}; +} + static std::expected getNextLine(std::istream& str, int& rawLineNum, int& lineNum) { std::string line = ""; std::string nextLine = ""; @@ -293,50 +307,44 @@ std::pair CConfig::configSetValueSafe(const std::string& com // TODO: all this sucks xD SSpecialCategory* overrideSpecialCat = nullptr; + const auto parsedName = parseConfigName(valueName.c_str()); - if (valueName.contains('[') && valueName.contains(']')) { - const auto L = valueName.find_first_of('['); - const auto R = valueName.find_last_of(']'); + if (!parsedName.category.empty()) { + impl->currentSpecialKey = parsedName.key; + valueName = parsedName.category + ":" + parsedName.name; - if (L < R) { - const auto CATKEY = valueName.substr(L + 1, R - L - 1); - impl->currentSpecialKey = CATKEY; + for (auto& sc : impl->specialCategoryDescriptors) { + if (sc->key.empty() || !valueName.starts_with(sc->name + ":")) + continue; - valueName = valueName.substr(0, L) + valueName.substr(R + 1); - - for (auto& sc : impl->specialCategoryDescriptors) { - if (sc->key.empty() || !valueName.starts_with(sc->name + ":")) + bool keyExists = false; + for (const auto& specialCat : impl->specialCategories) { + if (specialCat->key != sc->key || specialCat->name != sc->name) continue; - bool keyExists = false; - for (const auto& specialCat : impl->specialCategories) { - if (specialCat->key != sc->key || specialCat->name != sc->name) - continue; + if (parsedName.key != std::string_view{std::any_cast(specialCat->values[sc->key].getValue())}) + continue; - if (CATKEY != std::string_view{std::any_cast(specialCat->values[sc->key].getValue())}) - continue; - - // existing special - keyExists = true; - overrideSpecialCat = specialCat.get(); - } - - if (keyExists) - break; - - // if it doesn't exist, make it - const auto PCAT = impl->specialCategories.emplace_back(std::make_unique()).get(); - PCAT->descriptor = sc.get(); - PCAT->name = sc->name; - PCAT->key = sc->key; - addSpecialConfigValue(sc->name.c_str(), sc->key.c_str(), CConfigValue(CATKEY.c_str())); - - applyDefaultsToCat(*PCAT); - - PCAT->values[sc->key].setFrom(CATKEY); - overrideSpecialCat = PCAT; - break; + // existing special + keyExists = true; + overrideSpecialCat = specialCat.get(); } + + if (keyExists) + break; + + // if it doesn't exist, make it + const auto PCAT = impl->specialCategories.emplace_back(std::make_unique()).get(); + PCAT->descriptor = sc.get(); + PCAT->name = sc->name; + PCAT->key = sc->key; + addSpecialConfigValue(sc->name.c_str(), sc->key.c_str(), CConfigValue(parsedName.key.c_str())); + + applyDefaultsToCat(*PCAT); + + PCAT->values[sc->key].setFrom(parsedName.key); + overrideSpecialCat = PCAT; + break; } } @@ -1109,6 +1117,13 @@ CConfigValue* CConfig::getSpecialConfigValuePtr(const char* category, const char return nullptr; } +CConfigValue* CConfig::getAnyConfigValuePtr(const char* name) { + const auto parsedName = parseConfigName(name); + if (!parsedName.category.empty()) + return getSpecialConfigValuePtr(parsedName.category.c_str(), parsedName.name.c_str(), parsedName.key.c_str()); + return getConfigValuePtr(name); +} + void CConfig::registerHandler(PCONFIGHANDLERFUNC func, const char* name, SHandlerOptions options_) { SHandlerOptions options; std::memcpy(&options, &options_, seekABIStructSize(&options_, 0, sizeof(SHandlerOptions))); diff --git a/src/config.hpp b/src/config.hpp index 838a863..91c2522 100644 --- a/src/config.hpp +++ b/src/config.hpp @@ -70,6 +70,12 @@ struct SSpecialCategory { size_t anonymousID = 0; }; +struct SParsedConfigName { + std::string category = ""; + std::string key = ""; + std::string name = ""; +}; + enum eGetNextLineFailure : uint8_t { GETNEXTLINEFAILURE_EOF = 0, GETNEXTLINEFAILURE_BACKSLASH, diff --git a/tests/parse/main.cpp b/tests/parse/main.cpp index c3b0ef4..3ac57ee 100644 --- a/tests/parse/main.cpp +++ b/tests/parse/main.cpp @@ -385,6 +385,26 @@ int main(int argc, char** argv, char** envp) { EXPECT(std::any_cast(config.getSpecialConfigValue("specialAnonymousNested", "nested1:nested2:value1", KEYS2[1].c_str())), 12); EXPECT(std::any_cast(config.getSpecialConfigValue("specialAnonymousNested", "nested1:nested2:value2", KEYS2[1].c_str())), 13); + std::cout << " → Testing the unified config value getter\n"; + // check against expected counterpart + EXPECT(std::any_cast(config.getAnyConfigValue("testInt")), std::any_cast(config.getConfigValue("testInt"))); + EXPECT(std::any_cast(config.getAnyConfigValue("testFloat")), std::any_cast(config.getConfigValue("testFloat"))); + EXPECT(std::any_cast(config.getAnyConfigValue("testString")), std::any_cast(config.getConfigValue("testString"))); + EXPECT(std::any_cast(config.getAnyConfigValue("special[a]:value")), std::any_cast(config.getSpecialConfigValue("special", "value", "a"))); + EXPECT(std::any_cast(config.getAnyConfigValue("special[b]:value")), std::any_cast(config.getSpecialConfigValue("special", "value", "b"))); + EXPECT(std::any_cast(config.getAnyConfigValue("special[]:value")), std::any_cast(config.getSpecialConfigValue("special", "value", ""))); + EXPECT(std::any_cast(config.getAnyConfigValue("specialAnonymousNested[c]:nested:value1")), + std::any_cast(config.getSpecialConfigValue("specialAnonymousNested", "nested:value1", "c"))); + // check against malformed + EXPECT(config.getAnyConfigValuePtr("[a]:value"), nullptr); + EXPECT(config.getAnyConfigValuePtr("special[[a]:value"), nullptr); + EXPECT(config.getAnyConfigValuePtr("special[[a]]:value"), nullptr); + EXPECT(config.getAnyConfigValuePtr("special[a]]:value"), nullptr); + EXPECT(config.getAnyConfigValuePtr("special[a]value"), nullptr); + EXPECT(config.getAnyConfigValuePtr("special[avalue"), nullptr); + EXPECT(config.getAnyConfigValuePtr("special]:a[value"), nullptr); + EXPECT(config.getAnyConfigValuePtr("speciala]:value"), nullptr); + // test sourcing std::cout << " → Testing sourcing\n"; EXPECT(std::any_cast(config.getConfigValue("myColors:pink")), (Hyprlang::INT)0xFFc800c8);