From 78aaa5c1b448dc3d744cd6e8cc1a5ee466774107 Mon Sep 17 00:00:00 2001 From: tiosgz Date: Sat, 10 Jul 2021 20:22:37 +0000 Subject: [PATCH] Do not fail to parse a multi-bar config --- include/client.hpp | 1 + man/waybar.5.scd.in | 3 ++- src/client.cpp | 44 ++++++++++++++++++++++++++++++++++++-------- 3 files changed, 39 insertions(+), 9 deletions(-) diff --git a/include/client.hpp b/include/client.hpp index a54bf4b49..e7fa1db02 100644 --- a/include/client.hpp +++ b/include/client.hpp @@ -39,6 +39,7 @@ class Client { void handleOutput(struct waybar_output &output); bool isValidOutput(const Json::Value &config, struct waybar_output &output); auto setupConfig(const std::string &config_file, int depth) -> void; + auto resolveConfigIncludes(Json::Value &config, int depth) -> void; auto mergeConfig(Json::Value &a_config_, Json::Value &b_config_) -> void; auto setupCss(const std::string &css_file) -> void; struct waybar_output & getOutput(void *); diff --git a/man/waybar.5.scd.in b/man/waybar.5.scd.in index 6e6dd13ad..0168de386 100644 --- a/man/waybar.5.scd.in +++ b/man/waybar.5.scd.in @@ -86,8 +86,9 @@ Also a minimal example configuration can be found on the at the bottom of this m Only functional if compiled with gtk-layer-shell support. *include* ++ - typeof: array ++ + typeof: string|array ++ Paths to additional configuration files. In case of duplicate options, the including file's value takes precedence. Make sure to avoid circular imports. + For a multi-bar config, specify at least an empty object for each bar also in every file being included. # MODULE FORMAT diff --git a/src/client.cpp b/src/client.cpp index 07c0b23d6..ff6e7bf29 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -245,22 +245,50 @@ auto waybar::Client::setupConfig(const std::string &config_file, int depth) -> v std::string str((std::istreambuf_iterator(file)), std::istreambuf_iterator()); util::JsonParser parser; Json::Value tmp_config_ = parser.parse(str); - if (tmp_config_["include"].isArray()) { - for (const auto &include : tmp_config_["include"]) { + if (tmp_config_.isArray()) { + for (auto &config_part : tmp_config_) { + resolveConfigIncludes(config_part, depth); + } + } else { + resolveConfigIncludes(tmp_config_, depth); + } + mergeConfig(config_, tmp_config_); +} + +auto waybar::Client::resolveConfigIncludes(Json::Value &config, int depth) -> void { + Json::Value includes = config["include"]; + if (includes.isArray()) { + for (const auto &include : includes) { spdlog::info("Including resource file: {}", include.asString()); setupConfig(getValidPath({include.asString()}), ++depth); } + } else if (includes.isString()) { + spdlog::info("Including resource file: {}", includes.asString()); + setupConfig(getValidPath({includes.asString()}), ++depth); } - mergeConfig(config_, tmp_config_); } auto waybar::Client::mergeConfig(Json::Value &a_config_, Json::Value &b_config_) -> void { - for (const auto &key : b_config_.getMemberNames()) { - if (a_config_[key].type() == Json::objectValue && b_config_[key].type() == Json::objectValue) { - mergeConfig(a_config_[key], b_config_[key]); - } else { - a_config_[key] = b_config_[key]; + if (!a_config_) { + // For the first config + a_config_ = b_config_; + } else if (a_config_.isObject() && b_config_.isObject()) { + for (const auto &key : b_config_.getMemberNames()) { + if (a_config_[key].isObject() && b_config_[key].isObject()) { + mergeConfig(a_config_[key], b_config_[key]); + } else { + a_config_[key] = b_config_[key]; + } + } + } else if (a_config_.isArray() && b_config_.isArray()) { + // This can happen only on the top-level array of a multi-bar config + for (Json::Value::ArrayIndex i = 0; i < b_config_.size(); i++) { + if (a_config_[i].isObject() && b_config_[i].isObject()) { + mergeConfig(a_config_[i], b_config_[i]); + } } + } else { + spdlog::error("Cannot merge config, conflicting or invalid JSON types"); } }