config: try variables before handlers if possible

This commit is contained in:
Vaxry 2025-11-11 21:36:52 +00:00
parent 4dafa28d4f
commit 995db114b8
Signed by: vaxry
GPG key ID: 665806380871D640
4 changed files with 82 additions and 62 deletions

View file

@ -447,7 +447,7 @@ namespace Hyprlang {
CConfigImpl* impl; CConfigImpl* impl;
CParseResult parseLine(std::string line, bool dynamic = false); CParseResult parseLine(std::string line, bool dynamic = false);
CParseResult configSetValueSafe(const std::string& command, const std::string& value); std::pair<bool, CParseResult> configSetValueSafe(const std::string& command, const std::string& value);
CParseResult parseVariable(const std::string& lhs, const std::string& rhs, bool dynamic = false); CParseResult parseVariable(const std::string& lhs, const std::string& rhs, bool dynamic = false);
void clearState(); void clearState();
void applyDefaultsToCat(SSpecialCategory& cat); void applyDefaultsToCat(SSpecialCategory& cat);

View file

@ -279,7 +279,8 @@ static std::expected<int64_t, std::string> configStringToInt(const std::string&
return 0; return 0;
} }
CParseResult CConfig::configSetValueSafe(const std::string& command, const std::string& value) { // found, result
std::pair<bool, CParseResult> CConfig::configSetValueSafe(const std::string& command, const std::string& value) {
CParseResult result; CParseResult result;
std::string valueName; std::string valueName;
@ -358,7 +359,7 @@ CParseResult CConfig::configSetValueSafe(const std::string& command, const std::
if (VALUEIT != sc->values.end()) if (VALUEIT != sc->values.end())
found = true; found = true;
else if (sc->descriptor->dontErrorOnMissing) else if (sc->descriptor->dontErrorOnMissing)
return result; // will return a success, cuz we want to ignore missing return {true, result}; // will return a success, cuz we want to ignore missing
break; break;
} }
@ -407,7 +408,7 @@ CParseResult CConfig::configSetValueSafe(const std::string& command, const std::
} else { } else {
if (VALUEIT == PCAT->values.end() || VALUEIT->first != sc->key) { if (VALUEIT == PCAT->values.end() || VALUEIT->first != sc->key) {
result.setError(std::format("special category's first value must be the key. Key for <{}> is <{}>", PCAT->name, PCAT->key)); result.setError(std::format("special category's first value must be the key. Key for <{}> is <{}>", PCAT->name, PCAT->key));
return result; return {true, result};
} }
impl->currentSpecialKey = value; impl->currentSpecialKey = value;
} }
@ -418,7 +419,7 @@ CParseResult CConfig::configSetValueSafe(const std::string& command, const std::
if (!found) { if (!found) {
result.setError(std::format("config option <{}> does not exist.", valueName)); result.setError(std::format("config option <{}> does not exist.", valueName));
return result; return {false, result};
} }
} }
@ -428,7 +429,7 @@ CParseResult CConfig::configSetValueSafe(const std::string& command, const std::
const auto INT = configStringToInt(value); const auto INT = configStringToInt(value);
if (!INT.has_value()) { if (!INT.has_value()) {
result.setError(INT.error()); result.setError(INT.error());
return result; return {true, result};
} }
VALUEIT->second.setFrom(INT.value()); VALUEIT->second.setFrom(INT.value());
@ -440,7 +441,7 @@ CParseResult CConfig::configSetValueSafe(const std::string& command, const std::
VALUEIT->second.setFrom(std::stof(value)); VALUEIT->second.setFrom(std::stof(value));
} catch (std::exception& e) { } catch (std::exception& e) {
result.setError(std::format("failed parsing a float: {}", e.what())); result.setError(std::format("failed parsing a float: {}", e.what()));
return result; return {true, result};
} }
break; break;
} }
@ -458,7 +459,7 @@ CParseResult CConfig::configSetValueSafe(const std::string& command, const std::
VALUEIT->second.setFrom(SVector2D{.x = std::stof(LHS), .y = std::stof(RHS)}); VALUEIT->second.setFrom(SVector2D{.x = std::stof(LHS), .y = std::stof(RHS)});
} catch (std::exception& e) { } catch (std::exception& e) {
result.setError(std::format("failed parsing a vec2: {}", e.what())); result.setError(std::format("failed parsing a vec2: {}", e.what()));
return result; return {true, result};
} }
break; break;
} }
@ -473,19 +474,19 @@ CParseResult CConfig::configSetValueSafe(const std::string& command, const std::
if (RESULT.error) { if (RESULT.error) {
result.setError(RESULT.getError()); result.setError(RESULT.getError());
return result; return {true, result};
} }
break; break;
} }
default: { default: {
result.setError("internal error: invalid value found (no type?)"); result.setError("internal error: invalid value found (no type?)");
return result; return {true, result};
} }
} }
VALUEIT->second.m_bSetByUser = true; VALUEIT->second.m_bSetByUser = true;
return result; return {true, result};
} }
CParseResult CConfig::parseVariable(const std::string& lhs, const std::string& rhs, bool dynamic) { CParseResult CConfig::parseVariable(const std::string& lhs, const std::string& rhs, bool dynamic) {
@ -791,6 +792,13 @@ CParseResult CConfig::parseLine(std::string line, bool dynamic) {
bool found = false; bool found = false;
if (!impl->configOptions.verifyOnly) {
auto [f, rv] = configSetValueSafe(LHS, RHS);
found = f;
ret = std::move(rv);
}
if (!found) {
for (auto& h : impl->handlers) { for (auto& h : impl->handlers) {
// we want to handle potentially nested keywords and ensure // we want to handle potentially nested keywords and ensure
// we only call the handler if they are scoped correctly, // we only call the handler if they are scoped correctly,
@ -827,9 +835,7 @@ CParseResult CConfig::parseLine(std::string line, bool dynamic) {
ret = h.func(LHS.c_str(), RHS.c_str()); ret = h.func(LHS.c_str(), RHS.c_str());
found = true; found = true;
} }
}
if (!found && !impl->configOptions.verifyOnly)
ret = configSetValueSafe(LHS, RHS);
if (ret.error) if (ret.error)
return ret; return ret;

View file

@ -121,6 +121,7 @@ specialGeneric {
specialAnonymous { specialAnonymous {
value = 2 value = 2
testHandlerDontOverride = true
} }
specialAnonymous { specialAnonymous {

View file

@ -29,6 +29,7 @@ Hyprlang::CConfig* pConfig = nullptr;
std::string currentPath = ""; std::string currentPath = "";
std::string ignoreKeyword = ""; std::string ignoreKeyword = "";
std::string useKeyword = ""; std::string useKeyword = "";
bool testHandlerDontOverrideValue = false;
static std::vector<std::string> categoryKeywordActualValues; static std::vector<std::string> categoryKeywordActualValues;
static Hyprlang::CParseResult handleDoABarrelRoll(const char* COMMAND, const char* VALUE) { static Hyprlang::CParseResult handleDoABarrelRoll(const char* COMMAND, const char* VALUE) {
@ -74,6 +75,14 @@ static Hyprlang::CParseResult handleSource(const char* COMMAND, const char* VALU
return pConfig->parseFile(PATH.c_str()); return pConfig->parseFile(PATH.c_str());
} }
static Hyprlang::CParseResult handleTestHandlerDontOverride(const char* COMMAND, const char* VALUE) {
testHandlerDontOverrideValue = true;
Hyprlang::CParseResult result;
return result;
}
static Hyprlang::CParseResult handleCustomValueSet(const char* VALUE, void** data) { static Hyprlang::CParseResult handleCustomValueSet(const char* VALUE, void** data) {
if (!*data) if (!*data)
*data = calloc(1, sizeof(int64_t)); *data = calloc(1, sizeof(int64_t));
@ -147,12 +156,14 @@ int main(int argc, char** argv, char** envp) {
config.registerHandler(&handleTestUseKeyword, ":testUseKeyword", {.allowFlags = false}); config.registerHandler(&handleTestUseKeyword, ":testUseKeyword", {.allowFlags = false});
config.registerHandler(&handleNoop, "testCategory:testUseKeyword", {.allowFlags = false}); config.registerHandler(&handleNoop, "testCategory:testUseKeyword", {.allowFlags = false});
config.registerHandler(&handleCategoryKeyword, "testCategory:categoryKeyword", {.allowFlags = false}); config.registerHandler(&handleCategoryKeyword, "testCategory:categoryKeyword", {.allowFlags = false});
config.registerHandler(&handleTestHandlerDontOverride, "testHandlerDontOverride", {.allowFlags = false});
config.addSpecialCategory("special", {.key = "key"}); config.addSpecialCategory("special", {.key = "key"});
config.addSpecialConfigValue("special", "value", (Hyprlang::INT)0); config.addSpecialConfigValue("special", "value", (Hyprlang::INT)0);
config.addSpecialCategory("specialAnonymous", {.key = nullptr, .ignoreMissing = false, .anonymousKeyBased = true}); config.addSpecialCategory("specialAnonymous", {.key = nullptr, .ignoreMissing = false, .anonymousKeyBased = true});
config.addSpecialConfigValue("specialAnonymous", "value", (Hyprlang::INT)0); config.addSpecialConfigValue("specialAnonymous", "value", (Hyprlang::INT)0);
config.addSpecialConfigValue("specialAnonymous", "testHandlerDontOverride", (Hyprlang::INT)0);
config.addSpecialCategory("specialAnonymousNested", {.key = nullptr, .ignoreMissing = false, .anonymousKeyBased = true}); config.addSpecialCategory("specialAnonymousNested", {.key = nullptr, .ignoreMissing = false, .anonymousKeyBased = true});
config.addSpecialConfigValue("specialAnonymousNested", "nested:value1", (Hyprlang::INT)0); config.addSpecialConfigValue("specialAnonymousNested", "nested:value1", (Hyprlang::INT)0);
@ -239,6 +250,7 @@ int main(int argc, char** argv, char** envp) {
std::cout << " → Testing handlers\n"; std::cout << " → Testing handlers\n";
EXPECT(barrelRoll, true); EXPECT(barrelRoll, true);
EXPECT(flagsFound, std::string{"abc"}); EXPECT(flagsFound, std::string{"abc"});
EXPECT(testHandlerDontOverrideValue, false);
EXPECT(categoryKeywordActualValues.at(0), "we are having fun"); EXPECT(categoryKeywordActualValues.at(0), "we are having fun");
EXPECT(categoryKeywordActualValues.at(1), "so much fun"); EXPECT(categoryKeywordActualValues.at(1), "so much fun");
@ -315,6 +327,7 @@ int main(int argc, char** argv, char** envp) {
EXPECT(config.listKeysForSpecialCategory("specialAnonymous").size(), 2); EXPECT(config.listKeysForSpecialCategory("specialAnonymous").size(), 2);
const auto KEYS = config.listKeysForSpecialCategory("specialAnonymous"); const auto KEYS = config.listKeysForSpecialCategory("specialAnonymous");
EXPECT(std::any_cast<int64_t>(config.getSpecialConfigValue("specialAnonymous", "value", KEYS[0].c_str())), 2); EXPECT(std::any_cast<int64_t>(config.getSpecialConfigValue("specialAnonymous", "value", KEYS[0].c_str())), 2);
EXPECT(std::any_cast<int64_t>(config.getSpecialConfigValue("specialAnonymous", "testHandlerDontOverride", KEYS[0].c_str())), 1);
EXPECT(std::any_cast<int64_t>(config.getSpecialConfigValue("specialAnonymous", "value", KEYS[1].c_str())), 3); EXPECT(std::any_cast<int64_t>(config.getSpecialConfigValue("specialAnonymous", "value", KEYS[1].c_str())), 3);
// test anonymous nested // test anonymous nested