core: add options for config parser

This commit is contained in:
Vaxry 2023-12-30 13:30:46 +01:00
parent c69c286b31
commit 70145fd8da
5 changed files with 46 additions and 8 deletions

View file

@ -50,6 +50,15 @@ namespace Hyprlang {
friend class CConfig; friend class CConfig;
}; };
/* Generic struct for options for the config parser */
struct SConfigOptions {
/* Don't throw errors on missing values. */
bool verifyOnly = false;
/* Return all errors instead of the first */
bool throwAllErrors = false;
};
/* Generic struct for options for handlers */ /* Generic struct for options for handlers */
struct SHandlerOptions { struct SHandlerOptions {
bool allowFlags = false; bool allowFlags = false;
@ -142,7 +151,7 @@ namespace Hyprlang {
/* Base class for a config file */ /* Base class for a config file */
class CConfig { class CConfig {
public: public:
CConfig(const char* configPath); CConfig(const char* configPath, const SConfigOptions& options);
~CConfig(); ~CConfig();
/* Add a config value, for example myCategory:myValue. /* Add a config value, for example myCategory:myValue.

View file

@ -28,7 +28,7 @@ static std::string removeBeginEndSpacesTabs(std::string str) {
return str; return str;
} }
CConfig::CConfig(const char* path) { CConfig::CConfig(const char* path, const Hyprlang::SConfigOptions& options) {
impl = new CConfigImpl; impl = new CConfigImpl;
try { try {
impl->path = std::filesystem::canonical(path); impl->path = std::filesystem::canonical(path);
@ -46,6 +46,8 @@ CConfig::CConfig(const char* path) {
} }
std::sort(impl->envVariables.begin(), impl->envVariables.end(), [&](const auto& a, const auto& b) { return a.name.length() > b.name.length(); }); std::sort(impl->envVariables.begin(), impl->envVariables.end(), [&](const auto& a, const auto& b) { return a.name.length() > b.name.length(); });
impl->configOptions = options;
} }
CConfig::~CConfig() { CConfig::~CConfig() {
@ -444,7 +446,7 @@ CParseResult CConfig::parseLine(std::string line, bool dynamic) {
found = true; found = true;
} }
if (!found) if (!found && !impl->configOptions.verifyOnly)
ret = configSetValueSafe(LHS, RHS); ret = configSetValueSafe(LHS, RHS);
if (ret.error) if (ret.error)
@ -515,9 +517,11 @@ CParseResult CConfig::parseFile(std::string file) {
const auto RET = parseLine(line); const auto RET = parseLine(line);
if (RET.error && impl->parseError.empty()) { if (RET.error && (impl->parseError.empty() || impl->configOptions.throwAllErrors)) {
impl->parseError = RET.getError(); if (!impl->parseError.empty())
result.setError(std::format("Config error in file {} at line {}: {}", file, linenum, RET.errorStdString)); impl->parseError += "\n";
impl->parseError += std::format("Config error in file {} at line {}: {}", file, linenum, RET.errorStdString);
result.setError(impl->parseError);
} }
++linenum; ++linenum;
@ -526,7 +530,13 @@ CParseResult CConfig::parseFile(std::string file) {
iffile.close(); iffile.close();
if (!impl->categories.empty()) { if (!impl->categories.empty()) {
result.setError("Unclosed category at EOF"); if (impl->parseError.empty() || impl->configOptions.throwAllErrors) {
if (!impl->parseError.empty())
impl->parseError += "\n";
impl->parseError += std::format("Config error in file {}: Unclosed category at EOF", file);
result.setError(impl->parseError);
}
impl->categories.clear(); impl->categories.clear();
} }

View file

@ -69,4 +69,6 @@ class CConfigImpl {
std::string currentSpecialKey = ""; std::string currentSpecialKey = "";
std::string parseError = ""; std::string parseError = "";
Hyprlang::SConfigOptions configOptions;
}; };

6
tests/config/error.conf Normal file
View file

@ -0,0 +1,6 @@
# This config houses two errors
someValue = 123
category {
invalid_line

View file

@ -1,5 +1,6 @@
#include <iostream> #include <iostream>
#include <filesystem> #include <filesystem>
#include <algorithm>
#include <hyprlang.hpp> #include <hyprlang.hpp>
@ -75,7 +76,7 @@ int main(int argc, char** argv, char** envp) {
std::cout << "Starting test\n"; std::cout << "Starting test\n";
Hyprlang::CConfig config("./config/config.conf"); Hyprlang::CConfig config("./config/config.conf", {});
pConfig = &config; pConfig = &config;
currentPath = std::filesystem::canonical("./config/"); currentPath = std::filesystem::canonical("./config/");
@ -182,6 +183,16 @@ int main(int argc, char** argv, char** envp) {
std::cout << " → Testing custom types\n"; std::cout << " → Testing custom types\n";
EXPECT(*reinterpret_cast<int64_t*>(std::any_cast<void*>(config.getConfigValue("customType"))), 1L); EXPECT(*reinterpret_cast<int64_t*>(std::any_cast<void*>(config.getConfigValue("customType"))), 1L);
std::cout << " → Testing error.conf\n";
Hyprlang::CConfig errorConfig("./config/error.conf", {.verifyOnly = true, .throwAllErrors = true});
errorConfig.commence();
const auto ERRORS = errorConfig.parse();
EXPECT(ERRORS.error, true);
const auto ERRORSTR = std::string{ERRORS.getError()};
EXPECT(std::count(ERRORSTR.begin(), ERRORSTR.end(), '\n'), 1);
} 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;