From 095f54b910ed368160e9bef991ae201a5f642cd7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Brabant?= <72200344+aurelien-brabant@users.noreply.github.com> Date: Sun, 7 Jul 2024 18:05:23 +0200 Subject: [PATCH] core: handle scoped keywords if flags are not allowed (#49) * core: handle scoped keywords if flags are not allowed * chore: formatting * test: add test cases for unintended categoryKeyword config options * fix: use at() instead of [] --- src/config.cpp | 23 +++++++++++++++++++++-- tests/config/config.conf | 7 +++++++ tests/parse/main.cpp | 26 +++++++++++++++++++++----- 3 files changed, 49 insertions(+), 7 deletions(-) diff --git a/src/config.cpp b/src/config.cpp index a295650..c3add4c 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -563,9 +563,28 @@ CParseResult CConfig::parseLine(std::string line, bool dynamic) { return parseVariable(LHS, RHS, dynamic); bool found = false; + for (auto& h : impl->handlers) { - if (!h.options.allowFlags && h.name != LHS) - continue; + if (!h.options.allowFlags) { + // we want to handle potentially nested keywords and ensure + // we only call the handler if they are scoped correctly. + size_t colon = 0; + size_t idx = 0; + size_t depth = 0; + + while ((colon = h.name.find(":", idx)) != std::string::npos && impl->categories.size() > depth) { + auto actual = h.name.substr(idx, colon - idx); + + if (actual != impl->categories[depth]) + break; + + idx = colon + 1; + ++depth; + } + + if (depth != impl->categories.size() || h.name.substr(idx) != LHS) + continue; + } if (h.options.allowFlags && (!LHS.starts_with(h.name) || LHS.contains(':') /* avoid cases where a category is called the same as a handler */)) continue; diff --git a/tests/config/config.conf b/tests/config/config.conf index 45a7b8d..d94094a 100644 --- a/tests/config/config.conf +++ b/tests/config/config.conf @@ -28,6 +28,8 @@ errorVariable = true # hyprlang noerror false +categoryKeyword = oops, this one shouldn't call the handler, not fun + testCategory { testValueInt = 123456 testValueHex = 0xF @@ -41,7 +43,12 @@ testCategory { nested2 { testValueNest = 1 } + categoryKeyword = this one should not either } + + categoryKeyword = we are having fun + categoryKeyword = so much fun + categoryKeyword = im the fun one at parties } $SPECIALVAL1 = 1 diff --git a/tests/parse/main.cpp b/tests/parse/main.cpp index 6970386..bbc84b4 100644 --- a/tests/parse/main.cpp +++ b/tests/parse/main.cpp @@ -23,12 +23,13 @@ namespace Colors { } // globals for testing -bool barrelRoll = false; -std::string flagsFound = ""; -Hyprlang::CConfig* pConfig = nullptr; -std::string currentPath = ""; +bool barrelRoll = false; +std::string flagsFound = ""; +Hyprlang::CConfig* pConfig = nullptr; +std::string currentPath = ""; +static std::vector categoryKeywordActualValues; -static Hyprlang::CParseResult handleDoABarrelRoll(const char* COMMAND, const char* VALUE) { +static Hyprlang::CParseResult handleDoABarrelRoll(const char* COMMAND, const char* VALUE) { if (std::string(VALUE) == "woohoo, some, params") barrelRoll = true; @@ -44,6 +45,12 @@ static Hyprlang::CParseResult handleFlagsTest(const char* COMMAND, const char* V return result; } +static Hyprlang::CParseResult handleCategoryKeyword(const char* COMMAND, const char* VALUE) { + categoryKeywordActualValues.push_back(VALUE); + + return Hyprlang::CParseResult(); +} + static Hyprlang::CParseResult handleSource(const char* COMMAND, const char* VALUE) { std::string PATH = std::filesystem::canonical(currentPath + "/" + VALUE); return pConfig->parseFile(PATH.c_str()); @@ -88,12 +95,14 @@ int main(int argc, char** argv, char** envp) { config.addConfigValue("testStringColon", ""); config.addConfigValue("testEnv", ""); config.addConfigValue("testVar", (Hyprlang::INT)0); + config.addConfigValue("categoryKeyword", (Hyprlang::STRING) ""); config.addConfigValue("testStringQuotes", ""); config.addConfigValue("testStringRecursive", ""); config.addConfigValue("testCategory:testValueInt", (Hyprlang::INT)0); config.addConfigValue("testCategory:testValueHex", (Hyprlang::INT)0xA); config.addConfigValue("testCategory:nested1:testValueNest", (Hyprlang::INT)0); config.addConfigValue("testCategory:nested1:nested2:testValueNest", (Hyprlang::INT)0); + config.addConfigValue("testCategory:nested1:categoryKeyword", (Hyprlang::STRING) ""); config.addConfigValue("testDefault", (Hyprlang::INT)123); config.addConfigValue("testCategory:testColor1", (Hyprlang::INT)0); config.addConfigValue("testCategory:testColor2", (Hyprlang::INT)0); @@ -107,6 +116,7 @@ int main(int argc, char** argv, char** envp) { config.registerHandler(&handleDoABarrelRoll, "doABarrelRoll", {false}); config.registerHandler(&handleFlagsTest, "flags", {true}); config.registerHandler(&handleSource, "source", {false}); + config.registerHandler(&handleCategoryKeyword, "testCategory:categoryKeyword", {false}); config.addSpecialCategory("special", {"key"}); config.addSpecialConfigValue("special", "value", (Hyprlang::INT)0); @@ -149,6 +159,8 @@ int main(int argc, char** argv, char** envp) { EXPECT(std::any_cast(config.getConfigValue("testCategory:testColor2")), (Hyprlang::INT)0xFF000000); EXPECT(std::any_cast(config.getConfigValue("testCategory:testColor3")), (Hyprlang::INT)0x22ffeeff); EXPECT(std::any_cast(config.getConfigValue("testStringColon")), std::string{"ee:ee:ee"}); + EXPECT(std::any_cast(config.getConfigValue("categoryKeyword")), std::string{"oops, this one shouldn't call the handler, not fun"}); + EXPECT(std::any_cast(config.getConfigValue("testCategory:nested1:categoryKeyword")), std::string{"this one should not either"}); // test static values std::cout << " → Testing static values\n"; @@ -162,6 +174,10 @@ int main(int argc, char** argv, char** envp) { EXPECT(barrelRoll, true); EXPECT(flagsFound, std::string{"abc"}); + EXPECT(categoryKeywordActualValues.at(0), "we are having fun"); + EXPECT(categoryKeywordActualValues.at(1), "so much fun"); + EXPECT(categoryKeywordActualValues.at(2), "im the fun one at parties"); + // test dynamic std::cout << " → Testing dynamic\n"; barrelRoll = false;