Allow using wildcards in config include paths

Updates `Config::tryExpandPath()` to return a vector of expanded path
matches instead of a single path wrapped in an optional, with an empty
vector indicating no matches.

`Config::resolveConfigIncludes()` iterates over all of these matches,
while other instances of path expansion (such as finding the base config
path) retain their existing behavior and only use the first match.
This commit is contained in:
Ethan Martin
2025-01-02 20:50:39 -05:00
parent ac08b752e3
commit d1dac2854a
3 changed files with 24 additions and 18 deletions

View File

@ -20,7 +20,7 @@ class Config {
static std::optional<std::string> findConfigPath( static std::optional<std::string> findConfigPath(
const std::vector<std::string> &names, const std::vector<std::string> &dirs = CONFIG_DIRS); const std::vector<std::string> &names, const std::vector<std::string> &dirs = CONFIG_DIRS);
static std::optional<std::string> tryExpandPath(const std::string &base, static std::vector<std::string> tryExpandPath(const std::string &base,
const std::string &filename); const std::string &filename);
Config() = default; Config() = default;

View File

@ -68,11 +68,11 @@ ALabel::ALabel(const Json::Value& config, const std::string& name, const std::st
// there might be "~" or "$HOME" in original path, try to expand it. // there might be "~" or "$HOME" in original path, try to expand it.
auto result = Config::tryExpandPath(menuFile, ""); auto result = Config::tryExpandPath(menuFile, "");
if (!result.has_value()) { if (result.empty()) {
throw std::runtime_error("Failed to expand file: " + menuFile); throw std::runtime_error("Failed to expand file: " + menuFile);
} }
menuFile = result.value(); menuFile = result.front();
// Read the menu descriptor file // Read the menu descriptor file
std::ifstream file(menuFile); std::ifstream file(menuFile);
if (!file.is_open()) { if (!file.is_open()) {

View File

@ -21,7 +21,7 @@ const std::vector<std::string> Config::CONFIG_DIRS = {
const char *Config::CONFIG_PATH_ENV = "WAYBAR_CONFIG_DIR"; const char *Config::CONFIG_PATH_ENV = "WAYBAR_CONFIG_DIR";
std::optional<std::string> Config::tryExpandPath(const std::string &base, std::vector<std::string> Config::tryExpandPath(const std::string &base,
const std::string &filename) { const std::string &filename) {
fs::path path; fs::path path;
@ -33,33 +33,35 @@ std::optional<std::string> Config::tryExpandPath(const std::string &base,
spdlog::debug("Try expanding: {}", path.string()); spdlog::debug("Try expanding: {}", path.string());
std::vector<std::string> results;
wordexp_t p; wordexp_t p;
if (wordexp(path.c_str(), &p, 0) == 0) { if (wordexp(path.c_str(), &p, 0) == 0) {
if (access(*p.we_wordv, F_OK) == 0) { for (size_t i = 0; i < p.we_wordc; i++) {
std::string result = *p.we_wordv; if (access(p.we_wordv[i], F_OK) == 0) {
wordfree(&p); results.emplace_back(p.we_wordv[i]);
spdlog::debug("Found config file: {}", path.string()); spdlog::debug("Found config file: {}", p.we_wordv[i]);
return result; }
} }
wordfree(&p); wordfree(&p);
} }
return std::nullopt;
return results;
} }
std::optional<std::string> Config::findConfigPath(const std::vector<std::string> &names, std::optional<std::string> Config::findConfigPath(const std::vector<std::string> &names,
const std::vector<std::string> &dirs) { const std::vector<std::string> &dirs) {
if (const char *dir = std::getenv(Config::CONFIG_PATH_ENV)) { if (const char *dir = std::getenv(Config::CONFIG_PATH_ENV)) {
for (const auto &name : names) { for (const auto &name : names) {
if (auto res = tryExpandPath(dir, name); res) { if (auto res = tryExpandPath(dir, name); !res.empty()) {
return res; return res.front();
} }
} }
} }
for (const auto &dir : dirs) { for (const auto &dir : dirs) {
for (const auto &name : names) { for (const auto &name : names) {
if (auto res = tryExpandPath(dir, name); res) { if (auto res = tryExpandPath(dir, name); !res.empty()) {
return res; return res.front();
} }
} }
} }
@ -92,11 +94,15 @@ void Config::resolveConfigIncludes(Json::Value &config, int depth) {
if (includes.isArray()) { if (includes.isArray()) {
for (const auto &include : includes) { for (const auto &include : includes) {
spdlog::info("Including resource file: {}", include.asString()); spdlog::info("Including resource file: {}", include.asString());
setupConfig(config, tryExpandPath(include.asString(), "").value_or(""), ++depth); for (const auto &match : tryExpandPath(include.asString(), "")) {
setupConfig(config, match, depth + 1);
}
} }
} else if (includes.isString()) { } else if (includes.isString()) {
spdlog::info("Including resource file: {}", includes.asString()); spdlog::info("Including resource file: {}", includes.asString());
setupConfig(config, tryExpandPath(includes.asString(), "").value_or(""), ++depth); for (const auto &match : tryExpandPath(includes.asString(), "")) {
setupConfig(config, match, depth + 1);
}
} }
} }