core: add special categories

This commit is contained in:
Vaxry 2023-12-29 13:26:21 +01:00
parent 4d9da8db25
commit cc05e782b3
5 changed files with 208 additions and 11 deletions

View file

@ -59,6 +59,38 @@ void CConfig::addConfigValue(const char* name, const CConfigValue value) {
impl->defaultValues[std::string{name}] = value;
}
void CConfig::addSpecialConfigValue(const char* cat, const char* name, const CConfigValue value) {
const auto IT = std::find_if(impl->specialCategoryDescriptors.begin(), impl->specialCategoryDescriptors.end(), [&](const auto& other) { return other->name == cat; });
if (IT == impl->specialCategoryDescriptors.end())
throw "No such category";
IT->get()->defaultValues[std::string{name}] = value;
}
void CConfig::addSpecialCategory(const char* name, SSpecialCategoryOptions options) {
const auto PDESC = impl->specialCategoryDescriptors.emplace_back(std::make_unique<SSpecialCategoryDescriptor>()).get();
PDESC->name = name;
PDESC->key = options.key ? options.key : "";
PDESC->dontErrorOnMissing = options.ignoreMissing;
if (!options.key) {
const auto PCAT = impl->specialCategories.emplace_back(std::make_unique<SSpecialCategory>()).get();
PCAT->descriptor = PDESC;
PCAT->name = name;
PCAT->key = options.key ? options.key : "";
PCAT->isStatic = true;
if (!PCAT->key.empty())
addSpecialConfigValue(name, options.key, CConfigValue("0"));
}
}
void SSpecialCategory::applyDefaults() {
for (auto& [k, v] : descriptor->defaultValues) {
values[k] = v;
}
}
void CConfig::commence() {
m_bCommenced = true;
for (auto& [k, v] : impl->defaultValues) {
@ -174,11 +206,63 @@ CParseResult CConfig::configSetValueSafe(const std::string& command, const std::
valueName += command;
const auto VALUEIT = impl->values.find(valueName);
auto VALUEIT = impl->values.find(valueName);
if (VALUEIT == impl->values.end()) {
// it might be in a special category
bool found = false;
for (auto& sc : impl->specialCategories) {
if (!valueName.starts_with(sc->name) || valueName.substr(sc->name.length() + 1).contains(":"))
continue;
if (!sc->isStatic && std::string{std::any_cast<const char*>(sc->values[sc->key].getValue())} != impl->currentSpecialKey)
continue;
VALUEIT = sc->values.find(valueName.substr(sc->name.length() + 1));
if (VALUEIT != sc->values.end())
found = true;
else if (sc->descriptor->dontErrorOnMissing)
return result; // will return a success, cuz we want to ignore missing
break;
}
if (!found) {
// could be a dynamic category that doesnt exist yet
for (auto& sc : impl->specialCategoryDescriptors) {
if (sc->key.empty() || !valueName.starts_with(sc->name) || valueName.substr(sc->name.length() + 1).contains(":"))
continue;
// bingo
const auto PCAT = impl->specialCategories.emplace_back(std::make_unique<SSpecialCategory>()).get();
PCAT->descriptor = sc.get();
PCAT->name = sc->name;
PCAT->key = sc->key;
addSpecialConfigValue(sc->name.c_str(), sc->key.c_str(), CConfigValue("0"));
PCAT->applyDefaults();
VALUEIT = PCAT->values.find(valueName.substr(sc->name.length() + 1));
if (VALUEIT != PCAT->values.end())
found = true;
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));
return result;
}
impl->currentSpecialKey = value;
break;
}
}
if (!found) {
result.setError(std::format("config option <{}> does not exist.", valueName));
return result;
}
}
switch (VALUEIT->second.m_eType) {
case CConfigValue::eDataType::CONFIGDATATYPE_INT: {
@ -359,6 +443,7 @@ CParseResult CConfig::parseLine(std::string line, bool dynamic) {
return result;
}
impl->currentSpecialKey = "";
impl->categories.pop_back();
} else {
// open a category.
@ -385,6 +470,9 @@ CParseResult CConfig::parse() {
for (auto& [k, v] : impl->defaultValues) {
impl->values.at(k) = v;
}
for (auto& sc : impl->specialCategories) {
sc->applyDefaults();
}
std::ifstream iffile(impl->path);
if (!iffile.good())
@ -430,6 +518,7 @@ void CConfig::clearState() {
impl->categories.clear();
impl->parseError = "";
impl->variables = impl->envVariables;
std::erase_if(impl->specialCategories, [](const auto& e) { return !e->isStatic; });
}
CConfigValue* CConfig::getConfigValuePtr(const char* name) {
@ -437,6 +526,25 @@ CConfigValue* CConfig::getConfigValuePtr(const char* name) {
return IT == impl->values.end() ? nullptr : &IT->second;
}
CConfigValue* CConfig::getSpecialConfigValuePtr(const char* category, const char* name, const char* key) {
const std::string CAT = category;
const std::string NAME = name;
const std::string KEY = key ? key : "";
for (auto& sc : impl->specialCategories) {
if (sc->name != CAT || (!sc->isStatic && std::string{std::any_cast<const char*>(sc->values[sc->key].getValue())} != KEY))
continue;
const auto IT = sc->values.find(NAME);
if (IT == sc->values.end())
return nullptr;
return &IT->second;
}
return nullptr;
}
void CConfig::registerHandler(PCONFIGHANDLERFUNC func, const char* name, SHandlerOptions options) {
impl->handlers.push_back(SHandler{name, options, func});
}

View file

@ -16,6 +16,23 @@ struct SVariable {
std::vector<std::string> linesContainingVar; // for dynamic updates
};
struct SSpecialCategoryDescriptor {
std::string name = "";
std::string key = "";
std::unordered_map<std::string, Hyprlang::CConfigValue> defaultValues;
bool dontErrorOnMissing = false;
};
struct SSpecialCategory {
SSpecialCategoryDescriptor* descriptor = nullptr;
std::string name = "";
std::string key = ""; // empty means no key
std::unordered_map<std::string, Hyprlang::CConfigValue> values;
bool isStatic = false;
void applyDefaults();
};
class CConfigImpl {
public:
std::string path = "";
@ -25,8 +42,11 @@ class CConfigImpl {
std::vector<SHandler> handlers;
std::vector<SVariable> variables;
std::vector<SVariable> envVariables;
std::vector<std::unique_ptr<SSpecialCategory>> specialCategories;
std::vector<std::unique_ptr<SSpecialCategoryDescriptor>> specialCategoryDescriptors;
std::vector<std::string> categories;
std::string currentSpecialKey = "";
std::string parseError = "";
};

View file

@ -44,10 +44,22 @@ namespace Hyprlang {
friend class CConfig;
};
/* Generic struct for options for handlers */
struct SHandlerOptions {
bool allowFlags = false;
};
/* Generic struct for options for special categories */
struct SSpecialCategoryOptions {
/* a key is the name of a value that will be the identifier of a special category
can be left null for no key, aka a generic one
keys are always strings. Default key value is "0" */
const char* key = nullptr;
/* don't pop up an error if the config value is missing */
bool ignoreMissing = false;
};
/* typedefs */
typedef CParseResult (*PCONFIGHANDLERFUNC)(const char* COMMAND, const char* VALUE);
@ -111,6 +123,12 @@ namespace Hyprlang {
no new values may be added or removed. Required for parsing. */
void commence();
/* Add a special category. Can be done dynamically. */
void addSpecialCategory(const char* name, SSpecialCategoryOptions options);
/* Add a config value to a special category */
void addSpecialConfigValue(const char* cat, const char* name, const CConfigValue value);
/* Parse the config. Refresh the values. */
CParseResult parse();
@ -124,6 +142,12 @@ namespace Hyprlang {
nullptr on fail */
CConfigValue* getConfigValuePtr(const char* name);
/* Get a special category's config value ptr. These are only static for static (key-less)
categories, unless a new variable is added via addSpecialConfigValue.
key can be nullptr for static categories. Cannot be nullptr for id-based categories.
nullptr on fail. */
CConfigValue* getSpecialConfigValuePtr(const char* category, const char* name, const char* key = nullptr);
/* Get a config value's stored value. Empty on fail*/
std::any getConfigValue(const char* name) {
CConfigValue* val = getConfigValuePtr(name);
@ -132,6 +156,14 @@ namespace Hyprlang {
return val->getValue();
}
/* Get a special config value's stored value. Empty on fail. */
std::any getSpecialConfigValue(const char* category, const char* name, const char* key = nullptr) {
CConfigValue* val = getSpecialConfigValuePtr(category, name, key);
if (!val)
return {};
return val->getValue();
}
private:
bool m_bCommenced = false;

View file

@ -27,6 +27,27 @@ testCategory {
}
}
special {
key = a
value = 1
}
special {
key = b
value = 2
}
specialGeneric {
one {
value = 1
}
two {
value = 2
nonexistent = abc
}
}
testCategory:testValueHex = 0xFFfFaAbB
testStringQuotes = "Hello World!"

View file

@ -68,8 +68,16 @@ int main(int argc, char** argv, char** envp) {
config.registerHandler(&handleDoABarrelRoll, "doABarrelRoll", {false});
config.registerHandler(&handleFlagsTest, "flags", {true});
config.addSpecialCategory("special", {"key"});
config.addSpecialConfigValue("special", "value", 0L);
config.commence();
config.addSpecialCategory("specialGeneric:one", {nullptr, true});
config.addSpecialConfigValue("specialGeneric:one", "value", 0L);
config.addSpecialCategory("specialGeneric:two", {nullptr, true});
config.addSpecialConfigValue("specialGeneric:two", "value", 0L);
const auto PARSERESULT = config.parse();
if (PARSERESULT.error) {
std::cout << "Parse error: " << PARSERESULT.getError() << "\n";
@ -118,8 +126,16 @@ int main(int argc, char** argv, char** envp) {
EXPECT(std::any_cast<int64_t>(config.getConfigValue("testVar")), 1337420);
// test env variables
std::cout << " → Testing env variables\n";
EXPECT(std::any_cast<const char*>(config.getConfigValue("testEnv")), std::string{getenv("SHELL")});
// test special categories
std::cout << " → Testing special categories\n";
EXPECT(std::any_cast<int64_t>(config.getSpecialConfigValue("special", "value", "a")), 1);
EXPECT(std::any_cast<int64_t>(config.getSpecialConfigValue("special", "value", "b")), 2);
EXPECT(std::any_cast<int64_t>(config.getSpecialConfigValue("specialGeneric:one", "value")), 1);
EXPECT(std::any_cast<int64_t>(config.getSpecialConfigValue("specialGeneric:two", "value")), 2);
} catch (const char* e) {
std::cout << Colors::RED << "Error: " << Colors::RESET << e << "\n";
return 1;