Skip to content

Commit

Permalink
config and args: add --override
Browse files Browse the repository at this point in the history
It was an implementation taken from https://github.com/BurntRanch/TabAUR (our project) in v0.6.2
  • Loading branch information
Toni500github committed Feb 28, 2025
1 parent 01d4a01 commit 62754c7
Show file tree
Hide file tree
Showing 4 changed files with 128 additions and 30 deletions.
9 changes: 9 additions & 0 deletions customfetch.1
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,15 @@ Example: "customfetch -m "${auto}OS: $<os.name>" -m "${auto}CPU: $<cpu.cpu>" "
.br
Will only print the logo (if not disabled), along side the parsed OS and CPU
.TP
\fB\-O\fR, \fB\-\-override\fR <string>
Overrides a config value, but NOT arrays.
.br
Syntax must be "name=value" E.g "auto.disk.fmt='Disk(%1): %6'".
.br
For convinience purpose, names that doesn't have a dot for telling the table, will automatically be considered under the [config] table
.br
E.g "sep-reset-after=true" works as "config.sep-reset-after=true"
.TP
\fB\-p\fR, \fB\-\-logo-position\fR <value>
Position of the logo ("top" or "left" or "bottom")
.TP
Expand Down
48 changes: 45 additions & 3 deletions include/config.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,27 @@
#define TOML_HEADER_ONLY 0

#include <cstdint>
#include <type_traits>
#include <unordered_map>

#include "toml++/toml.hpp"
#include "util.hpp"

enum types
{
STR,
BOOL,
INT
};

struct override_configs_types
{
types value_type;
std::string string_value = "";
bool bool_value = false;
int int_value = 0;
};

// config colors
// those without gui_ prefix are for the terminal
struct colors_t
Expand Down Expand Up @@ -60,8 +77,7 @@ class Config
{
public:
// Create .config directories and files and load the config file (args or default)
Config(const std::string_view configFile, const std::string_view configDir, colors_t& colors,
bool do_not_load = false);
Config(const std::string_view configFile, const std::string_view configDir);

// Variables of config file in [config] table
std::vector<std::string> layout;
Expand Down Expand Up @@ -115,6 +131,8 @@ class Config
bool m_display_distro = true;
bool m_print_logo_only = false;

std::unordered_map<std::string, override_configs_types> overrides;

/**
* Load config file and parse every config variables
* @param filename The config file path
Expand All @@ -137,6 +155,14 @@ class Config
*/
void addAliasColors(const std::string& str);

/**
* Override a config value from --override
* @param str The value to override.
* Must have a '=' for separating the name and value to override.
* NO spaces between
*/
void overrideOption(const std::string& opt);

private:
// Parsed config from loadConfigFile()
toml::table tbl;
Expand All @@ -149,7 +175,23 @@ class Config
template <typename T>
T getValue(const std::string_view value, const T&& fallback, bool dont_expand_var = false) const
{
std::optional<T> ret = this->tbl.at_path(value).value<T>();
const auto& overridePos = overrides.find(value.data());

// user wants a bool (overridable), we found an override matching the name, and the override is a bool.
if constexpr (std::is_same<T, bool>())
if (overridePos != overrides.end() && overrides.at(value.data()).value_type == BOOL)
return overrides.at(value.data()).bool_value;

// user wants a str (overridable), we found an override matching the name, and the override is a str.
if constexpr (std::is_same<T, std::string>())
if (overridePos != overrides.end() && overrides.at(value.data()).value_type == STR)
return overrides.at(value.data()).string_value;

if constexpr (std::is_same<T, std::uint16_t>())
if (overridePos != overrides.end() && overrides.at(value.data()).value_type == INT)
return overrides.at(value.data()).int_value;

const std::optional<T> ret = this->tbl.at_path(value).value<T>();
if constexpr (toml::is_string<T>) // if we want to get a value that's a string
return ret ? expandVar(ret.value(), dont_expand_var) : expandVar(fallback, dont_expand_var);
else
Expand Down
48 changes: 41 additions & 7 deletions src/config.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,14 @@

#include <cstdlib>
#include <filesystem>
#include <string>

#include "fmt/os.h"
#include "query.hpp"
#include "switch_fnv1a.hpp"
#include "util.hpp"

Config::Config(const std::string_view configFile, const std::string_view configDir, colors_t& colors, bool do_not_load)
Config::Config(const std::string_view configFile, const std::string_view configDir)
{
if (!std::filesystem::exists(configDir))
{
Expand All @@ -46,11 +47,6 @@ Config::Config(const std::string_view configFile, const std::string_view configD
warn(_("config file {} not found, generating new one"), configFile);
this->generateConfig(configFile);
}

if (!do_not_load)
{
this->loadConfigFile(configFile, colors);
}
}

void Config::loadConfigFile(const std::string_view filename, colors_t& colors)
Expand Down Expand Up @@ -195,12 +191,50 @@ void Config::addAliasColors(const std::string& str)
this->colors_value.push_back(value);
}

static bool is_str_digital(const std::string& str)
{
for (size_t i = 0; i < str.size(); ++i)
if (!(str[i] >= '0' && str[i] <= '9'))
return false;

return true;
}

void Config::overrideOption(const std::string& opt)
{
const size_t pos = opt.find('=');
if (pos == std::string::npos)
die(_("alias color '{}' does NOT have an equal sign '=' for separating color name and value\n"
"For more check with --help"), opt);

std::string name {opt.substr(0, pos)};
const std::string& value = opt.substr(pos + 1);

// usually the user finds incovinient to write "config.foo"
// for general config options
if (name.find('.') == name.npos)
name.insert(0, "config.");

if (value == "true")
overrides[name] = {.value_type = BOOL, .bool_value = true};
else if (value == "false")
overrides[name] = {.value_type = BOOL, .bool_value = false};
else if ((value[0] == '"' && value.back() == '"') ||
(value[0] == '\'' && value.back() == '\''))
overrides[name] = {.value_type = STR, .string_value = value.substr(1, value.size()-2)};
else if (is_str_digital(value))
overrides[name] = {.value_type = INT, .int_value = std::stoi(value)};
else
die(_("looks like override value '{}' from '{}' is neither a bool, int or string value"),
value, name);
}

void Config::generateConfig(const std::string_view filename)
{
#if !ANDROID_APP
if (std::filesystem::exists(filename))
{
if (!askUserYorN(false, "WARNING: config file {} already exists. Do you want to overwrite it?", filename))
if (!askUserYorN(false, "WARNING: config file '{}' already exists. Do you want to overwrite it?", filename))
std::exit(1);
}
#endif
Expand Down
53 changes: 33 additions & 20 deletions src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,11 @@ NOTE: Arguments that takes [<bool>] values, the values can be either: "true", 1,
Example: `customfetch -m "${auto}OS: $<os.name>" -m "${auto}CPU: $<cpu.cpu>"`
Will only print the logo (if not disabled), along side the parsed OS and CPU
-O, --override <string> Overrides a config value, but NOT arrays.
Syntax must be "name=value" E.g "auto.disk.fmt='Disk(%1): %6'".
For convinience purpose, names that doesn't have a dot for telling the table, will automatically be considered under the [config] table
E.g "sep-reset-after=true" works as "config.sep-reset-after=true"
-p, --logo-position <value> Position of the logo ("top" or "left" or "bottom")
-o, --offset <num> Offset between the ascii art and the layout
-l, --list-modules Print the list of the info tag modules and its members
Expand Down Expand Up @@ -525,7 +530,7 @@ static STRING_IF_ANDROID_APP_ELSE(bool) parseargs(int argc, char* argv[], Config
int opt = 0;
int option_index = 0;
opterr = 1; // re-enable since before we disabled for "invalid option" error
const char *optstring = "-VhwnLlNa::f:o:C:i:d:D:p:s:m:";
const char *optstring = "-VhwnLlNa::f:o:C:O:i:d:D:p:s:m:";
static const struct option opts[] = {
{"version", no_argument, 0, 'V'},
{"help", no_argument, 0, 'h'},
Expand All @@ -536,6 +541,7 @@ static STRING_IF_ANDROID_APP_ELSE(bool) parseargs(int argc, char* argv[], Config
{"no-color", optional_argument, 0, 'N'},
{"ascii-logo-type", optional_argument, 0, 'a'},
{"offset", required_argument, 0, 'o'},
{"override", required_argument, 0, 'O'},
{"font", required_argument, 0, 'f'},
{"config", required_argument, 0, 'C'},
{"layout-line", required_argument, 0, 'm'},
Expand Down Expand Up @@ -583,23 +589,25 @@ static STRING_IF_ANDROID_APP_ELSE(bool) parseargs(int argc, char* argv[], Config
case "list-logos"_fnv1a16:
RETURN_IF_ANDROID_APP list_logos(config); break;
case 'f':
config.font = optarg; break;
config.overrides["gui.font"] = {.value_type = STR, .string_value = optarg}; break;
case 'o':
config.offset = std::stoi(optarg); break;
config.overrides["config.offset"] = {.value_type = INT, .int_value = std::stoi(optarg)}; break;
case 'C': // we have already did it in parse_config_path()
break;
case 'D':
config.data_dir = optarg; break;
config.overrides["config.data-dir"] = {.value_type = STR, .string_value = optarg}; break;
case 'd':
config.m_custom_distro = str_tolower(optarg); break;
case 'm':
config.m_args_layout.push_back(optarg); break;
case 'p':
config.logo_position = optarg; break;
config.overrides["config.logo-position"] = {.value_type = STR, .string_value = optarg}; break;
case 's':
config.source_path = optarg; break;
config.overrides["config.source-path"] = {.value_type = STR, .string_value = optarg}; break;
case 'i':
config.m_image_backend = optarg; break;
case 'O':
config.overrideOption(optarg); break;
case 'N':
if (OPTIONAL_ARGUMENT_IS_PRESENT)
config.m_disable_colors = str_to_bool(optarg);
Expand All @@ -608,9 +616,9 @@ static STRING_IF_ANDROID_APP_ELSE(bool) parseargs(int argc, char* argv[], Config
break;
case 'a':
if (OPTIONAL_ARGUMENT_IS_PRESENT)
config.ascii_logo_type = optarg;
config.overrides["config.ascii-logo-type"] = {.value_type = STR, .string_value = optarg};
else
config.ascii_logo_type.clear();
config.overrides["config.ascii-logo-type"] = {.value_type = STR, .string_value = ""};
break;
case 'n':
if (OPTIONAL_ARGUMENT_IS_PRESENT)
Expand All @@ -626,25 +634,25 @@ static STRING_IF_ANDROID_APP_ELSE(bool) parseargs(int argc, char* argv[], Config
break;

case "logo-padding-top"_fnv1a16:
config.logo_padding_top = std::stoi(optarg); break;
config.overrides["config.logo-padding-top"] = {.value_type = INT, .int_value = std::stoi(optarg)}; break;

case "logo-padding-left"_fnv1a16:
config.logo_padding_left = std::stoi(optarg); break;
config.overrides["config.logo-padding-left"] = {.value_type = INT, .int_value = std::stoi(optarg)}; break;

case "layout-padding-top"_fnv1a16:
config.layout_padding_top = std::stoi(optarg); break;
config.overrides["config.layout-padding-top"] = {.value_type = INT, .int_value = std::stoi(optarg)}; break;

case "loop-ms"_fnv1a16:
config.loop_ms = std::stoul(optarg); break;

case "bg-image"_fnv1a16:
config.gui_bg_image = optarg; break;
config.overrides["gui.bg-image"] = {.value_type = STR, .string_value = optarg}; break;

case "wrap-lines"_fnv1a16:
if (OPTIONAL_ARGUMENT_IS_PRESENT)
config.wrap_lines = str_to_bool(optarg);
config.overrides["config.wrap-lines"] = {.value_type = BOOL, .bool_value = str_to_bool(optarg)};
else
config.wrap_lines = true;
config.overrides["config.wrap-lines"] = {.value_type = BOOL, .bool_value = true};
break;

case "add-color"_fnv1a16:
Expand All @@ -658,16 +666,16 @@ static STRING_IF_ANDROID_APP_ELSE(bool) parseargs(int argc, char* argv[], Config
exit(EXIT_SUCCESS);

case "sep-reset"_fnv1a16:
config.sep_reset = optarg; break;
config.overrides["config.sep-reset"] = {.value_type = STR, .string_value = optarg}; break;

case "title-sep"_fnv1a16:
config.title_sep = optarg; break;
config.overrides["config.title-sep"] = {.value_type = STR, .string_value = optarg}; break;

case "sep-reset-after"_fnv1a16:
if (OPTIONAL_ARGUMENT_IS_PRESENT)
config.sep_reset_after = str_to_bool(optarg);
config.overrides["config.sep-reset-after"] = {.value_type = BOOL, .bool_value = str_to_bool(optarg)};
else
config.sep_reset_after = true;
config.overrides["config.sep-reset-after"] = {.value_type = BOOL, .bool_value = true};
break;

default:
Expand Down Expand Up @@ -740,19 +748,24 @@ int main(int argc, char *argv[])
localize();

#if ANDROID_APP
Config config(configFile, configDir, colors, do_not_load_config);
Config config(configFile, configDir);
const std::string& parseargs_ret = parseargs(argc, argv, config, configFile);
if (parseargs_ret != _true)
return parseargs_ret;

if (!do_not_load_config)
config.loadConfigFile(configFile, colors);

// since ANDROID_APP means that it will run as an android widget, so in GUI,
// then let's make it always true
config.gui = true;
config.wrap_lines = true;
#else
Config config(configFile, configDir, colors, false);
Config config(configFile, configDir);
if (!parseargs(argc, argv, config, configFile))
return 1;

config.loadConfigFile(configFile, colors);
#endif // ANDROID_APP

is_live_mode = (config.loop_ms > 50);
Expand Down

0 comments on commit 62754c7

Please sign in to comment.