mirror of
https://github.com/hyprwm/hyprlang.git
synced 2024-11-17 02:25:59 +01:00
core: add special categories
This commit is contained in:
parent
4d9da8db25
commit
cc05e782b3
5 changed files with 208 additions and 11 deletions
114
src/config.cpp
114
src/config.cpp
|
@ -59,6 +59,38 @@ void CConfig::addConfigValue(const char* name, const CConfigValue value) {
|
||||||
impl->defaultValues[std::string{name}] = 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() {
|
void CConfig::commence() {
|
||||||
m_bCommenced = true;
|
m_bCommenced = true;
|
||||||
for (auto& [k, v] : impl->defaultValues) {
|
for (auto& [k, v] : impl->defaultValues) {
|
||||||
|
@ -174,10 +206,62 @@ CParseResult CConfig::configSetValueSafe(const std::string& command, const std::
|
||||||
|
|
||||||
valueName += command;
|
valueName += command;
|
||||||
|
|
||||||
const auto VALUEIT = impl->values.find(valueName);
|
auto VALUEIT = impl->values.find(valueName);
|
||||||
if (VALUEIT == impl->values.end()) {
|
if (VALUEIT == impl->values.end()) {
|
||||||
result.setError(std::format("config option <{}> does not exist.", valueName));
|
// it might be in a special category
|
||||||
return result;
|
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) {
|
switch (VALUEIT->second.m_eType) {
|
||||||
|
@ -359,6 +443,7 @@ CParseResult CConfig::parseLine(std::string line, bool dynamic) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl->currentSpecialKey = "";
|
||||||
impl->categories.pop_back();
|
impl->categories.pop_back();
|
||||||
} else {
|
} else {
|
||||||
// open a category.
|
// open a category.
|
||||||
|
@ -385,6 +470,9 @@ CParseResult CConfig::parse() {
|
||||||
for (auto& [k, v] : impl->defaultValues) {
|
for (auto& [k, v] : impl->defaultValues) {
|
||||||
impl->values.at(k) = v;
|
impl->values.at(k) = v;
|
||||||
}
|
}
|
||||||
|
for (auto& sc : impl->specialCategories) {
|
||||||
|
sc->applyDefaults();
|
||||||
|
}
|
||||||
|
|
||||||
std::ifstream iffile(impl->path);
|
std::ifstream iffile(impl->path);
|
||||||
if (!iffile.good())
|
if (!iffile.good())
|
||||||
|
@ -430,6 +518,7 @@ void CConfig::clearState() {
|
||||||
impl->categories.clear();
|
impl->categories.clear();
|
||||||
impl->parseError = "";
|
impl->parseError = "";
|
||||||
impl->variables = impl->envVariables;
|
impl->variables = impl->envVariables;
|
||||||
|
std::erase_if(impl->specialCategories, [](const auto& e) { return !e->isStatic; });
|
||||||
}
|
}
|
||||||
|
|
||||||
CConfigValue* CConfig::getConfigValuePtr(const char* name) {
|
CConfigValue* CConfig::getConfigValuePtr(const char* name) {
|
||||||
|
@ -437,6 +526,25 @@ CConfigValue* CConfig::getConfigValuePtr(const char* name) {
|
||||||
return IT == impl->values.end() ? nullptr : &IT->second;
|
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) {
|
void CConfig::registerHandler(PCONFIGHANDLERFUNC func, const char* name, SHandlerOptions options) {
|
||||||
impl->handlers.push_back(SHandler{name, options, func});
|
impl->handlers.push_back(SHandler{name, options, func});
|
||||||
}
|
}
|
|
@ -16,17 +16,37 @@ struct SVariable {
|
||||||
std::vector<std::string> linesContainingVar; // for dynamic updates
|
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 {
|
class CConfigImpl {
|
||||||
public:
|
public:
|
||||||
std::string path = "";
|
std::string path = "";
|
||||||
|
|
||||||
std::unordered_map<std::string, Hyprlang::CConfigValue> values;
|
std::unordered_map<std::string, Hyprlang::CConfigValue> values;
|
||||||
std::unordered_map<std::string, Hyprlang::CConfigValue> defaultValues;
|
std::unordered_map<std::string, Hyprlang::CConfigValue> defaultValues;
|
||||||
std::vector<SHandler> handlers;
|
std::vector<SHandler> handlers;
|
||||||
std::vector<SVariable> variables;
|
std::vector<SVariable> variables;
|
||||||
std::vector<SVariable> envVariables;
|
std::vector<SVariable> envVariables;
|
||||||
|
std::vector<std::unique_ptr<SSpecialCategory>> specialCategories;
|
||||||
|
std::vector<std::unique_ptr<SSpecialCategoryDescriptor>> specialCategoryDescriptors;
|
||||||
|
|
||||||
std::vector<std::string> categories;
|
std::vector<std::string> categories;
|
||||||
|
std::string currentSpecialKey = "";
|
||||||
|
|
||||||
std::string parseError = "";
|
std::string parseError = "";
|
||||||
};
|
};
|
|
@ -44,10 +44,22 @@ namespace Hyprlang {
|
||||||
friend class CConfig;
|
friend class CConfig;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* Generic struct for options for handlers */
|
||||||
struct SHandlerOptions {
|
struct SHandlerOptions {
|
||||||
bool allowFlags = false;
|
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 */
|
/* typedefs */
|
||||||
typedef CParseResult (*PCONFIGHANDLERFUNC)(const char* COMMAND, const char* VALUE);
|
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. */
|
no new values may be added or removed. Required for parsing. */
|
||||||
void commence();
|
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. */
|
/* Parse the config. Refresh the values. */
|
||||||
CParseResult parse();
|
CParseResult parse();
|
||||||
|
|
||||||
|
@ -124,6 +142,12 @@ namespace Hyprlang {
|
||||||
nullptr on fail */
|
nullptr on fail */
|
||||||
CConfigValue* getConfigValuePtr(const char* name);
|
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*/
|
/* Get a config value's stored value. Empty on fail*/
|
||||||
std::any getConfigValue(const char* name) {
|
std::any getConfigValue(const char* name) {
|
||||||
CConfigValue* val = getConfigValuePtr(name);
|
CConfigValue* val = getConfigValuePtr(name);
|
||||||
|
@ -132,6 +156,14 @@ namespace Hyprlang {
|
||||||
return val->getValue();
|
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:
|
private:
|
||||||
bool m_bCommenced = false;
|
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
|
testCategory:testValueHex = 0xFFfFaAbB
|
||||||
|
|
||||||
testStringQuotes = "Hello World!"
|
testStringQuotes = "Hello World!"
|
||||||
|
|
|
@ -68,8 +68,16 @@ int main(int argc, char** argv, char** envp) {
|
||||||
config.registerHandler(&handleDoABarrelRoll, "doABarrelRoll", {false});
|
config.registerHandler(&handleDoABarrelRoll, "doABarrelRoll", {false});
|
||||||
config.registerHandler(&handleFlagsTest, "flags", {true});
|
config.registerHandler(&handleFlagsTest, "flags", {true});
|
||||||
|
|
||||||
|
config.addSpecialCategory("special", {"key"});
|
||||||
|
config.addSpecialConfigValue("special", "value", 0L);
|
||||||
|
|
||||||
config.commence();
|
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();
|
const auto PARSERESULT = config.parse();
|
||||||
if (PARSERESULT.error) {
|
if (PARSERESULT.error) {
|
||||||
std::cout << "Parse error: " << PARSERESULT.getError() << "\n";
|
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);
|
EXPECT(std::any_cast<int64_t>(config.getConfigValue("testVar")), 1337420);
|
||||||
|
|
||||||
// test env variables
|
// test env variables
|
||||||
|
std::cout << " → Testing env variables\n";
|
||||||
EXPECT(std::any_cast<const char*>(config.getConfigValue("testEnv")), std::string{getenv("SHELL")});
|
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) {
|
} catch (const char* e) {
|
||||||
std::cout << Colors::RED << "Error: " << Colors::RESET << e << "\n";
|
std::cout << Colors::RED << "Error: " << Colors::RESET << e << "\n";
|
||||||
return 1;
|
return 1;
|
||||||
|
|
Loading…
Reference in a new issue