mirror of
https://github.com/hyprwm/hyprlang.git
synced 2024-12-22 09:59:48 +01:00
core: add special categories
This commit is contained in:
parent
4d9da8db25
commit
cc05e782b3
5 changed files with 208 additions and 11 deletions
110
src/config.cpp
110
src/config.cpp
|
@ -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});
|
||||
}
|
|
@ -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 = "";
|
||||
};
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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!"
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in a new issue