Skip to content

Commit 62754c7

Browse files
committed
config and args: add --override
It was an implementation taken from https://github.com/BurntRanch/TabAUR (our project) in v0.6.2
1 parent 01d4a01 commit 62754c7

File tree

4 files changed

+128
-30
lines changed

4 files changed

+128
-30
lines changed

customfetch.1

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,15 @@ Example: "customfetch -m "${auto}OS: $<os.name>" -m "${auto}CPU: $<cpu.cpu>" "
152152
.br
153153
Will only print the logo (if not disabled), along side the parsed OS and CPU
154154
.TP
155+
\fB\-O\fR, \fB\-\-override\fR <string>
156+
Overrides a config value, but NOT arrays.
157+
.br
158+
Syntax must be "name=value" E.g "auto.disk.fmt='Disk(%1): %6'".
159+
.br
160+
For convinience purpose, names that doesn't have a dot for telling the table, will automatically be considered under the [config] table
161+
.br
162+
E.g "sep-reset-after=true" works as "config.sep-reset-after=true"
163+
.TP
155164
\fB\-p\fR, \fB\-\-logo-position\fR <value>
156165
Position of the logo ("top" or "left" or "bottom")
157166
.TP

include/config.hpp

Lines changed: 45 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,27 @@
2929
#define TOML_HEADER_ONLY 0
3030

3131
#include <cstdint>
32+
#include <type_traits>
33+
#include <unordered_map>
3234

3335
#include "toml++/toml.hpp"
3436
#include "util.hpp"
3537

38+
enum types
39+
{
40+
STR,
41+
BOOL,
42+
INT
43+
};
44+
45+
struct override_configs_types
46+
{
47+
types value_type;
48+
std::string string_value = "";
49+
bool bool_value = false;
50+
int int_value = 0;
51+
};
52+
3653
// config colors
3754
// those without gui_ prefix are for the terminal
3855
struct colors_t
@@ -60,8 +77,7 @@ class Config
6077
{
6178
public:
6279
// Create .config directories and files and load the config file (args or default)
63-
Config(const std::string_view configFile, const std::string_view configDir, colors_t& colors,
64-
bool do_not_load = false);
80+
Config(const std::string_view configFile, const std::string_view configDir);
6581

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

134+
std::unordered_map<std::string, override_configs_types> overrides;
135+
118136
/**
119137
* Load config file and parse every config variables
120138
* @param filename The config file path
@@ -137,6 +155,14 @@ class Config
137155
*/
138156
void addAliasColors(const std::string& str);
139157

158+
/**
159+
* Override a config value from --override
160+
* @param str The value to override.
161+
* Must have a '=' for separating the name and value to override.
162+
* NO spaces between
163+
*/
164+
void overrideOption(const std::string& opt);
165+
140166
private:
141167
// Parsed config from loadConfigFile()
142168
toml::table tbl;
@@ -149,7 +175,23 @@ class Config
149175
template <typename T>
150176
T getValue(const std::string_view value, const T&& fallback, bool dont_expand_var = false) const
151177
{
152-
std::optional<T> ret = this->tbl.at_path(value).value<T>();
178+
const auto& overridePos = overrides.find(value.data());
179+
180+
// user wants a bool (overridable), we found an override matching the name, and the override is a bool.
181+
if constexpr (std::is_same<T, bool>())
182+
if (overridePos != overrides.end() && overrides.at(value.data()).value_type == BOOL)
183+
return overrides.at(value.data()).bool_value;
184+
185+
// user wants a str (overridable), we found an override matching the name, and the override is a str.
186+
if constexpr (std::is_same<T, std::string>())
187+
if (overridePos != overrides.end() && overrides.at(value.data()).value_type == STR)
188+
return overrides.at(value.data()).string_value;
189+
190+
if constexpr (std::is_same<T, std::uint16_t>())
191+
if (overridePos != overrides.end() && overrides.at(value.data()).value_type == INT)
192+
return overrides.at(value.data()).int_value;
193+
194+
const std::optional<T> ret = this->tbl.at_path(value).value<T>();
153195
if constexpr (toml::is_string<T>) // if we want to get a value that's a string
154196
return ret ? expandVar(ret.value(), dont_expand_var) : expandVar(fallback, dont_expand_var);
155197
else

src/config.cpp

Lines changed: 41 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,14 @@
2727

2828
#include <cstdlib>
2929
#include <filesystem>
30+
#include <string>
3031

3132
#include "fmt/os.h"
3233
#include "query.hpp"
3334
#include "switch_fnv1a.hpp"
3435
#include "util.hpp"
3536

36-
Config::Config(const std::string_view configFile, const std::string_view configDir, colors_t& colors, bool do_not_load)
37+
Config::Config(const std::string_view configFile, const std::string_view configDir)
3738
{
3839
if (!std::filesystem::exists(configDir))
3940
{
@@ -46,11 +47,6 @@ Config::Config(const std::string_view configFile, const std::string_view configD
4647
warn(_("config file {} not found, generating new one"), configFile);
4748
this->generateConfig(configFile);
4849
}
49-
50-
if (!do_not_load)
51-
{
52-
this->loadConfigFile(configFile, colors);
53-
}
5450
}
5551

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

194+
static bool is_str_digital(const std::string& str)
195+
{
196+
for (size_t i = 0; i < str.size(); ++i)
197+
if (!(str[i] >= '0' && str[i] <= '9'))
198+
return false;
199+
200+
return true;
201+
}
202+
203+
void Config::overrideOption(const std::string& opt)
204+
{
205+
const size_t pos = opt.find('=');
206+
if (pos == std::string::npos)
207+
die(_("alias color '{}' does NOT have an equal sign '=' for separating color name and value\n"
208+
"For more check with --help"), opt);
209+
210+
std::string name {opt.substr(0, pos)};
211+
const std::string& value = opt.substr(pos + 1);
212+
213+
// usually the user finds incovinient to write "config.foo"
214+
// for general config options
215+
if (name.find('.') == name.npos)
216+
name.insert(0, "config.");
217+
218+
if (value == "true")
219+
overrides[name] = {.value_type = BOOL, .bool_value = true};
220+
else if (value == "false")
221+
overrides[name] = {.value_type = BOOL, .bool_value = false};
222+
else if ((value[0] == '"' && value.back() == '"') ||
223+
(value[0] == '\'' && value.back() == '\''))
224+
overrides[name] = {.value_type = STR, .string_value = value.substr(1, value.size()-2)};
225+
else if (is_str_digital(value))
226+
overrides[name] = {.value_type = INT, .int_value = std::stoi(value)};
227+
else
228+
die(_("looks like override value '{}' from '{}' is neither a bool, int or string value"),
229+
value, name);
230+
}
231+
198232
void Config::generateConfig(const std::string_view filename)
199233
{
200234
#if !ANDROID_APP
201235
if (std::filesystem::exists(filename))
202236
{
203-
if (!askUserYorN(false, "WARNING: config file {} already exists. Do you want to overwrite it?", filename))
237+
if (!askUserYorN(false, "WARNING: config file '{}' already exists. Do you want to overwrite it?", filename))
204238
std::exit(1);
205239
}
206240
#endif

src/main.cpp

Lines changed: 33 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,11 @@ NOTE: Arguments that takes [<bool>] values, the values can be either: "true", 1,
117117
Example: `customfetch -m "${auto}OS: $<os.name>" -m "${auto}CPU: $<cpu.cpu>"`
118118
Will only print the logo (if not disabled), along side the parsed OS and CPU
119119
120+
-O, --override <string> Overrides a config value, but NOT arrays.
121+
Syntax must be "name=value" E.g "auto.disk.fmt='Disk(%1): %6'".
122+
For convinience purpose, names that doesn't have a dot for telling the table, will automatically be considered under the [config] table
123+
E.g "sep-reset-after=true" works as "config.sep-reset-after=true"
124+
120125
-p, --logo-position <value> Position of the logo ("top" or "left" or "bottom")
121126
-o, --offset <num> Offset between the ascii art and the layout
122127
-l, --list-modules Print the list of the info tag modules and its members
@@ -525,7 +530,7 @@ static STRING_IF_ANDROID_APP_ELSE(bool) parseargs(int argc, char* argv[], Config
525530
int opt = 0;
526531
int option_index = 0;
527532
opterr = 1; // re-enable since before we disabled for "invalid option" error
528-
const char *optstring = "-VhwnLlNa::f:o:C:i:d:D:p:s:m:";
533+
const char *optstring = "-VhwnLlNa::f:o:C:O:i:d:D:p:s:m:";
529534
static const struct option opts[] = {
530535
{"version", no_argument, 0, 'V'},
531536
{"help", no_argument, 0, 'h'},
@@ -536,6 +541,7 @@ static STRING_IF_ANDROID_APP_ELSE(bool) parseargs(int argc, char* argv[], Config
536541
{"no-color", optional_argument, 0, 'N'},
537542
{"ascii-logo-type", optional_argument, 0, 'a'},
538543
{"offset", required_argument, 0, 'o'},
544+
{"override", required_argument, 0, 'O'},
539545
{"font", required_argument, 0, 'f'},
540546
{"config", required_argument, 0, 'C'},
541547
{"layout-line", required_argument, 0, 'm'},
@@ -583,23 +589,25 @@ static STRING_IF_ANDROID_APP_ELSE(bool) parseargs(int argc, char* argv[], Config
583589
case "list-logos"_fnv1a16:
584590
RETURN_IF_ANDROID_APP list_logos(config); break;
585591
case 'f':
586-
config.font = optarg; break;
592+
config.overrides["gui.font"] = {.value_type = STR, .string_value = optarg}; break;
587593
case 'o':
588-
config.offset = std::stoi(optarg); break;
594+
config.overrides["config.offset"] = {.value_type = INT, .int_value = std::stoi(optarg)}; break;
589595
case 'C': // we have already did it in parse_config_path()
590596
break;
591597
case 'D':
592-
config.data_dir = optarg; break;
598+
config.overrides["config.data-dir"] = {.value_type = STR, .string_value = optarg}; break;
593599
case 'd':
594600
config.m_custom_distro = str_tolower(optarg); break;
595601
case 'm':
596602
config.m_args_layout.push_back(optarg); break;
597603
case 'p':
598-
config.logo_position = optarg; break;
604+
config.overrides["config.logo-position"] = {.value_type = STR, .string_value = optarg}; break;
599605
case 's':
600-
config.source_path = optarg; break;
606+
config.overrides["config.source-path"] = {.value_type = STR, .string_value = optarg}; break;
601607
case 'i':
602608
config.m_image_backend = optarg; break;
609+
case 'O':
610+
config.overrideOption(optarg); break;
603611
case 'N':
604612
if (OPTIONAL_ARGUMENT_IS_PRESENT)
605613
config.m_disable_colors = str_to_bool(optarg);
@@ -608,9 +616,9 @@ static STRING_IF_ANDROID_APP_ELSE(bool) parseargs(int argc, char* argv[], Config
608616
break;
609617
case 'a':
610618
if (OPTIONAL_ARGUMENT_IS_PRESENT)
611-
config.ascii_logo_type = optarg;
619+
config.overrides["config.ascii-logo-type"] = {.value_type = STR, .string_value = optarg};
612620
else
613-
config.ascii_logo_type.clear();
621+
config.overrides["config.ascii-logo-type"] = {.value_type = STR, .string_value = ""};
614622
break;
615623
case 'n':
616624
if (OPTIONAL_ARGUMENT_IS_PRESENT)
@@ -626,25 +634,25 @@ static STRING_IF_ANDROID_APP_ELSE(bool) parseargs(int argc, char* argv[], Config
626634
break;
627635

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

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

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

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

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

643651
case "wrap-lines"_fnv1a16:
644652
if (OPTIONAL_ARGUMENT_IS_PRESENT)
645-
config.wrap_lines = str_to_bool(optarg);
653+
config.overrides["config.wrap-lines"] = {.value_type = BOOL, .bool_value = str_to_bool(optarg)};
646654
else
647-
config.wrap_lines = true;
655+
config.overrides["config.wrap-lines"] = {.value_type = BOOL, .bool_value = true};
648656
break;
649657

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

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

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

666674
case "sep-reset-after"_fnv1a16:
667675
if (OPTIONAL_ARGUMENT_IS_PRESENT)
668-
config.sep_reset_after = str_to_bool(optarg);
676+
config.overrides["config.sep-reset-after"] = {.value_type = BOOL, .bool_value = str_to_bool(optarg)};
669677
else
670-
config.sep_reset_after = true;
678+
config.overrides["config.sep-reset-after"] = {.value_type = BOOL, .bool_value = true};
671679
break;
672680

673681
default:
@@ -740,19 +748,24 @@ int main(int argc, char *argv[])
740748
localize();
741749

742750
#if ANDROID_APP
743-
Config config(configFile, configDir, colors, do_not_load_config);
751+
Config config(configFile, configDir);
744752
const std::string& parseargs_ret = parseargs(argc, argv, config, configFile);
745753
if (parseargs_ret != _true)
746754
return parseargs_ret;
747755

756+
if (!do_not_load_config)
757+
config.loadConfigFile(configFile, colors);
758+
748759
// since ANDROID_APP means that it will run as an android widget, so in GUI,
749760
// then let's make it always true
750761
config.gui = true;
751762
config.wrap_lines = true;
752763
#else
753-
Config config(configFile, configDir, colors, false);
764+
Config config(configFile, configDir);
754765
if (!parseargs(argc, argv, config, configFile))
755766
return 1;
767+
768+
config.loadConfigFile(configFile, colors);
756769
#endif // ANDROID_APP
757770

758771
is_live_mode = (config.loop_ms > 50);

0 commit comments

Comments
 (0)