diff --git a/include/hyprlang.hpp b/include/hyprlang.hpp index 96bca49..9264fab 100644 --- a/include/hyprlang.hpp +++ b/include/hyprlang.hpp @@ -107,6 +107,13 @@ namespace Hyprlang { Don't throw on a missing config file. Carry on as if nothing happened. */ bool allowMissingConfig = false; + + /*! + \since 0.4.2 + + Treat configPath as a raw config stream. + */ + bool pathIsStream = false; }; /*! @@ -429,6 +436,7 @@ namespace Hyprlang { void clearState(); void applyDefaultsToCat(SSpecialCategory& cat); void retrieveKeysForCat(const char* category, const char*** out, size_t* len); + CParseResult parseRawStream(const std::string& stream); }; }; #endif \ No newline at end of file diff --git a/src/config.cpp b/src/config.cpp index f377d57..e342228 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -6,6 +6,7 @@ #include #include #include +#include using namespace Hyprlang; extern "C" char** environ; @@ -34,10 +35,14 @@ static std::string removeBeginEndSpacesTabs(std::string str) { } CConfig::CConfig(const char* path, const Hyprlang::SConfigOptions& options) { - impl = new CConfigImpl; - impl->path = path; + impl = new CConfigImpl; - if (!std::filesystem::exists(impl->path)) { + if (options.pathIsStream) + impl->rawConfigString = path; + else + impl->path = path; + + if (!options.pathIsStream && !std::filesystem::exists(impl->path)) { if (!options.allowMissingConfig) throw "File does not exist"; } @@ -593,24 +598,65 @@ CParseResult CConfig::parse() { applyDefaultsToCat(*sc); } - bool fileExists = std::filesystem::exists(impl->path); + CParseResult fileParseResult; - // implies options.allowMissingConfig - if (impl->configOptions.allowMissingConfig && !fileExists) - return CParseResult{}; - else if (!fileExists) { - CParseResult res; - res.setError("Config file is missing"); - return res; + if (impl->rawConfigString.empty()) { + bool fileExists = std::filesystem::exists(impl->path); + + // implies options.allowMissingConfig + if (impl->configOptions.allowMissingConfig && !fileExists) + return CParseResult{}; + else if (!fileExists) { + CParseResult res; + res.setError("Config file is missing"); + return res; + } + + std::string canonical = std::filesystem::canonical(impl->path); + + fileParseResult = parseFile(canonical.c_str()); + } else { + fileParseResult = parseRawStream(impl->rawConfigString); } - std::string canonical = std::filesystem::canonical(impl->path); - - CParseResult fileParseResult = parseFile(canonical.c_str()); - return fileParseResult; } +CParseResult CConfig::parseRawStream(const std::string& stream) { + CParseResult result; + + std::string line = ""; + int linenum = 1; + + std::stringstream str(stream); + + while (std::getline(str, line)) { + const auto RET = parseLine(line); + + if (RET.error && (impl->parseError.empty() || impl->configOptions.throwAllErrors)) { + if (!impl->parseError.empty()) + impl->parseError += "\n"; + impl->parseError += std::format("Config error at line {}: {}", linenum, RET.errorStdString); + result.setError(impl->parseError); + } + + ++linenum; + } + + if (!impl->categories.empty()) { + if (impl->parseError.empty() || impl->configOptions.throwAllErrors) { + if (!impl->parseError.empty()) + impl->parseError += "\n"; + impl->parseError += std::format("Config error: Unclosed category at EOF"); + result.setError(impl->parseError); + } + + impl->categories.clear(); + } + + return result; +} + CParseResult CConfig::parseFile(const char* file) { CParseResult result; diff --git a/src/config.hpp b/src/config.hpp index dbca25c..80d21b1 100644 --- a/src/config.hpp +++ b/src/config.hpp @@ -66,8 +66,11 @@ struct SSpecialCategory { class CConfigImpl { public: - std::string path = ""; - std::string originalPath = ""; + std::string path = ""; + std::string originalPath = ""; + + // if not-empty, used instead of path + std::string rawConfigString = ""; std::unordered_map values; std::unordered_map defaultValues;