refactor: clean up arg code more (#1377)

* fix sort function

* Try macros

* Revert "Try macros"

This reverts commit 93c7306f8a.

* use next_help_heading instead

* test

* Revert "test"

This reverts commit 017a1702b3.

* Reapply "test"

This reverts commit 23c09d3828.

* use foldl instead :)

* a word

* different conditionals

* update docs, change ordering

* more formatting stuff
This commit is contained in:
Clement Tsang 2024-01-11 00:25:53 -05:00 committed by GitHub
parent 6b62176554
commit bc2c363a49
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 500 additions and 510 deletions

View File

@ -78,7 +78,7 @@ As (yet another) process/system visualization and management application, bottom
- [Cross-platform support](https://github.com/ClementTsang/bottom#support) for Linux, macOS, and Windows, with more planned in the future.
- [Customizable behaviour](https://clementtsang.github.io/bottom/nightly/configuration/command-line-flags/) that can be controlled with command-line flags or a config file, such as:
- [Customizable behaviour](https://clementtsang.github.io/bottom/nightly/configuration/command-line-options/) that can be controlled with command-line flags or a config file, such as:
- Custom and built-in colour themes
- Customizing widget behaviour

View File

@ -1,47 +0,0 @@
# Command-line Flags
The following flags can be provided to bottom in the command line to change the behaviour of the program. You can also
see information on these flags by running `btm -h`, or run `btm --help` to display more detailed information on each flag:
| Flag | Behaviour |
| ----------------------------------- | --------------------------------------------------------------------- |
| --autohide_time | Temporarily shows the time scale in graphs. |
| -b, --basic | Hides graphs and uses a more basic look. |
| --battery | Shows the battery widget. |
| -S, --case_sensitive | Enables case sensitivity by default. |
| -c, --celsius | Sets the temperature type to Celsius. |
| --color <COLOR SCHEME> | Use a color scheme, use --help for info. |
| -C, --config <CONFIG PATH> | Sets the location of the config file. |
| -u, --current_usage | Sets process CPU% to be based on current CPU%. |
| -t, --default_time_value <TIME> | Default time value for graphs. |
| --default_widget_count <INT> | Sets the n'th selected widget type as the default. |
| --default_widget_type <WIDGET TYPE> | Sets the default widget type, use --help for info. |
| --disable_advanced_kill | Hides advanced process killing. |
| --disable_click | Disables mouse clicks. |
| -m, --dot_marker | Uses a dot marker for graphs. |
| --enable_cache_memory | Enable collecting and displaying cache and buffer memory. |
| --enable_gpu | Enable collecting and displaying GPU usage. |
| -e, --expanded | Expand the default widget upon starting the app. |
| -f, --fahrenheit | Sets the temperature type to Fahrenheit. |
| -g, --group_processes | Groups processes with the same name by default. |
| -a, --hide_avg_cpu | Hides the average CPU usage. |
| --hide_table_gap | Hides spacing between table headers and entries. |
| --hide_time | Hides the time scale. |
| -k, --kelvin | Sets the temperature type to Kelvin. |
| -l, --left_legend | Puts the CPU chart legend to the left side. |
| --mem_as_value | Defaults to showing process memory usage by value. |
| --network_use_binary_prefix | Displays the network widget with binary prefixes. |
| --network_use_bytes | Displays the network widget using bytes. |
| --network_use_log | Displays the network widget with a log scale. |
| --process_command | Show processes as their commands by default. |
| -r, --rate <TIME> | Sets the data refresh rate. |
| -R, --regex | Enables regex by default. |
| --retention <TIME> | The timespan of data stored. |
| --show_table_scroll_position | Shows the scroll position tracker in table widgets. |
| -d, --time_delta <TIME> | The amount of time changed upon zooming. |
| -T, --tree | Defaults the process widget be in tree mode. |
| -n, --unnormalized_cpu | Show process CPU% usage without normalizing over the number of cores. |
| --use_old_network_legend | DEPRECATED - uses a separate network legend. |
| -V, --version | Prints version information. |
| -W, --whole_word | Enables whole-word matching by default. |
| -h, --help | Print help (see more with '--help') |

View File

@ -0,0 +1,94 @@
# Command-line Options
The following options can be provided to bottom in the command line to change the behaviour of the program. You can also
see information on these options by running `btm -h`, or run `btm --help` to display more detailed information on each option:
## General Options
| Option | Behaviour |
| ----------------------------------- | --------------------------------------------------- |
| --autohide_time | Temporarily shows the time scale in graphs. |
| -b, --basic | Hides graphs and uses a more basic look. |
| -C, --config <CONFIG PATH> | Sets the location of the config file. |
| -t, --default_time_value <TIME> | Default time value for graphs. |
| --default_widget_count <INT> | Sets the n'th selected widget type as the default. |
| --default_widget_type <WIDGET TYPE> | Sets the default widget type, use --help for info. |
| --disable_click | Disables mouse clicks. |
| -m, --dot_marker | Uses a dot marker for graphs. |
| -e, --expanded | Expand the default widget upon starting the app. |
| --hide_table_gap | Hides spacing between table headers and entries. |
| --hide_time | Hides the time scale. |
| -l, --left_legend | Puts the CPU chart legend to the left side. |
| -r, --rate <TIME> | Sets the data refresh rate. |
| --retention <TIME> | The timespan of data stored. |
| --show_table_scroll_position | Shows the scroll position tracker in table widgets. |
| -d, --time_delta <TIME> | The amount of time changed upon zooming. |
## Process Options
| Option | Behaviour |
| ----------------------- | --------------------------------------------------------------------- |
| -S, --case_sensitive | Enables case sensitivity by default. |
| -u, --current_usage | Sets process CPU% to be based on current CPU%. |
| --disable_advanced_kill | Hides advanced process killing. |
| -g, --group_processes | Groups processes with the same name by default. |
| --process_command | Show processes as their commands by default. |
| -R, --regex | Enables regex by default. |
| -T, --tree | Defaults the process widget be in tree mode. |
| -n, --unnormalized_cpu | Show process CPU% usage without normalizing over the number of cores. |
| -W, --whole_word | Enables whole-word matching by default. |
## Temperature Options
| Option | Behaviour |
| ---------------- | --------------------------------------- |
| -c, --celsius | Use Celsius as the temperature unit. |
| -f, --fahrenheit | Use Fahrenheit as the temperature unit. |
| -k, --kelvin | Use Kelvin as the temperature unit. |
## CPU Options
| Option | Behaviour |
| ------------------ | ---------------------------- |
| -a, --hide_avg_cpu | Hides the average CPU usage. |
## Memory Options
| Option | Behaviour |
| --------------------- | --------------------------------------------------------- |
| --enable_cache_memory | Enable collecting and displaying cache and buffer memory. |
| --mem_as_value | Defaults to showing process memory usage by value. |
## Network Options
| Option | Behaviour |
| --------------------------- | ------------------------------------------------- |
| --network_use_binary_prefix | Displays the network widget with binary prefixes. |
| --network_use_bytes | Displays the network widget using bytes. |
| --network_use_log | Displays the network widget with a log scale. |
| --use_old_network_legend | DEPRECATED - uses a separate network legend. |
## Battery Options
| Option | Behaviour |
| --------- | ------------------------- |
| --battery | Shows the battery widget. |
## GPU Options
| Option | Behaviour |
| ------------ | ------------------------------------------- |
| --enable_gpu | Enable collecting and displaying GPU usage. |
## Style Options
| Option | Behaviour |
| ---------------------- | ---------------------------------------- |
| --color <COLOR SCHEME> | Use a color scheme, use --help for info. |
## Other Options
| Option | Behaviour |
| ------------- | ------------------------------------------ |
| -h, --help | Prints help (see more info with '--help'). |
| -V, --version | Prints version information. |

View File

@ -4,7 +4,7 @@
This section is in progress, and is just copied from the old documentation.
Most of the [command line flags](../command-line-flags.md) have config file equivalents to avoid having to type them out
Most of the [command line flags](../command-line-options.md) have config file equivalents to avoid having to type them out
each time:
| Field | Type | Functionality |

View File

@ -31,7 +31,7 @@ which contains a list of all the installation methods.
You can refer to the [usage](usage/general-usage.md) pages on how to use bottom (e.g. keybinds, some features, a general overview of what each widget does).
To configure bottom, refer to the [configuration](configuration/command-line-flags.md) pages (e.g. how it behaves, how it looks).
To configure bottom, refer to the [configuration](configuration/command-line-options.md) pages (e.g. how it behaves, how it looks).
## Contribution

View File

@ -12,7 +12,7 @@ received and transmitted.
The legend displays the current reads and writes per second in bits, as well as the total amount read/written.
The y-axis automatically scales based on shown read/write values, and by default, is a linear scale based on base-10 units (e.x. kilobit, gigabit, etc.).
Through [configuration](../../configuration/command-line-flags.md), the read/write per second unit can be changed to bytes, while the y-axis can be changed to a
Through [configuration](../../configuration/command-line-options.md), the read/write per second unit can be changed to bytes, while the y-axis can be changed to a
log scale and/or use base-2 units (e.x. kibibit, gibibit, etc.).
One can also adjust the displayed time range through either the keyboard or mouse, with a range of 30s to 600s.

View File

@ -140,7 +140,7 @@ nav:
- "Temperature Widget": usage/widgets/temperature.md
- "Battery Widget": usage/widgets/battery.md
- "Configuration":
- "Command-line Flags": configuration/command-line-flags.md
- "Command-line Flags": configuration/command-line-options.md
- "Config File":
- "Default Config": configuration/config-file/default-config.md
- "Flags": configuration/config-file/flags.md

View File

@ -3,6 +3,8 @@
//! Note that you probably want to keep this as a single file so the build script doesn't
//! trip all over itself.
use std::cmp::Ordering;
use clap::{builder::PossibleValuesParser, *};
const DEFAULT_WIDGET_TYPE_STR: &str = {
@ -80,132 +82,119 @@ pub fn get_matches() -> ArgMatches {
build_app().get_matches()
}
trait CommandBuilder {
fn general_args(self) -> Self;
/// Returns an [`Ordering`] for two [`Arg`] values.
///
/// Note this assumes that they both have a _long_ name, and will
/// panic if either are missing!
fn sort_args(a: &Arg, b: &Arg) -> Ordering {
let a = a.get_long().unwrap();
let b = b.get_long().unwrap();
fn style_args(self) -> Self;
fn temperature_args(self) -> Self;
fn process_args(self) -> Self;
fn cpu_args(self) -> Self;
fn mem_args(self) -> Self;
fn network_args(self) -> Self;
fn battery_args(self) -> Self;
fn gpu_args(self) -> Self;
fn other(self) -> Self;
a.cmp(b)
}
impl CommandBuilder for Command {
fn general_args(self) -> Command {
const HEADING: &str = "General Options";
/// Create an array of [`Arg`] values. If there is more than one value, then
/// they will be sorted by their long name. Note this sort will panic if
/// any [`Arg`] does not have a long name!
macro_rules! args {
( $arg:expr $(,)?) => {
[$arg]
};
( $( $arg:expr ),+ $(,)? ) => {
{
let mut args = [ $( $arg, )* ];
args.sort_unstable_by(sort_args);
args
}
};
}
let autohide_time = Arg::new("autohide_time")
.long("autohide_time")
.action(ArgAction::SetTrue)
.help("Temporarily shows the time scale in graphs.")
.long_help(
"Automatically hides the time scale in graphs after being shown for \
fn general_args(cmd: Command) -> Command {
let cmd = cmd.next_help_heading("General Options");
let autohide_time = Arg::new("autohide_time")
.long("autohide_time")
.action(ArgAction::SetTrue)
.help("Temporarily shows the time scale in graphs.")
.long_help(
"Automatically hides the time scale in graphs after being shown for \
a brief moment when zoomed in/out. If time is disabled via --hide_time \
then this will have no effect.",
)
.help_heading(HEADING);
);
let basic = Arg::new("basic")
.short('b')
.long("basic")
.action(ArgAction::SetTrue)
.help("Hides graphs and uses a more basic look.")
.long_help(
"Hides graphs and uses a more basic look. Design is largely inspired by htop's.",
)
.help_heading(HEADING);
let basic = Arg::new("basic")
.short('b')
.long("basic")
.action(ArgAction::SetTrue)
.help("Hides graphs and uses a more basic look.")
.long_help(
"Hides graphs and uses a more basic look. Design is largely inspired by htop's.",
);
let disable_click = Arg::new("disable_click")
.long("disable_click")
.action(ArgAction::SetTrue)
.help("Disables mouse clicks.")
.long_help("Disables mouse clicks from interacting with the program.")
.help_heading(HEADING);
let disable_click = Arg::new("disable_click")
.long("disable_click")
.action(ArgAction::SetTrue)
.help("Disables mouse clicks.")
.long_help("Disables mouse clicks from interacting with the program.");
let dot_marker = Arg::new("dot_marker")
.short('m')
.long("dot_marker")
.action(ArgAction::SetTrue)
.help("Uses a dot marker for graphs.")
.long_help("Uses a dot marker for graphs as opposed to the default braille marker.")
.help_heading(HEADING);
let dot_marker = Arg::new("dot_marker")
.short('m')
.long("dot_marker")
.action(ArgAction::SetTrue)
.help("Uses a dot marker for graphs.")
.long_help("Uses a dot marker for graphs as opposed to the default braille marker.");
let hide_table_gap = Arg::new("hide_table_gap")
.long("hide_table_gap")
.action(ArgAction::SetTrue)
.help("Hides spacing between table headers and entries.")
.long_help("Hides the spacing between table headers and entries.")
.help_heading(HEADING);
let hide_table_gap = Arg::new("hide_table_gap")
.long("hide_table_gap")
.action(ArgAction::SetTrue)
.help("Hides spacing between table headers and entries.")
.long_help("Hides the spacing between table headers and entries.");
let hide_time = Arg::new("hide_time")
.long("hide_time")
.action(ArgAction::SetTrue)
.help("Hides the time scale.")
.long_help("Completely hides the time scale from being shown.")
.help_heading(HEADING);
let hide_time = Arg::new("hide_time")
.long("hide_time")
.action(ArgAction::SetTrue)
.help("Hides the time scale.")
.long_help("Completely hides the time scale from being shown.");
let left_legend = Arg::new("left_legend")
.short('l')
.long("left_legend")
.action(ArgAction::SetTrue)
.help("Puts the CPU chart legend to the left side.")
.long_help("Puts the CPU chart legend to the left side rather than the right side.")
.help_heading(HEADING);
let left_legend = Arg::new("left_legend")
.short('l')
.long("left_legend")
.action(ArgAction::SetTrue)
.help("Puts the CPU chart legend to the left side.")
.long_help("Puts the CPU chart legend to the left side rather than the right side.");
let show_table_scroll_position = Arg::new("show_table_scroll_position")
.long("show_table_scroll_position")
.action(ArgAction::SetTrue)
.help("Shows the scroll position tracker in table widgets.")
.long_help(
"Shows the list scroll position tracker in the widget title for table widgets.",
)
.help_heading(HEADING);
let show_table_scroll_position = Arg::new("show_table_scroll_position")
.long("show_table_scroll_position")
.action(ArgAction::SetTrue)
.help("Shows the scroll position tracker in table widgets.")
.long_help("Shows the list scroll position tracker in the widget title for table widgets.");
let config_location = Arg::new("config_location")
.short('C')
.long("config")
.action(ArgAction::Set)
.value_name("CONFIG PATH")
.help("Sets the location of the config file.")
.long_help(
"Sets the location of the config file. Expects a config file in the TOML format. \
If it doesn't exist, one is created.",
)
.value_hint(ValueHint::AnyPath)
.help_heading(HEADING);
let config_location = Arg::new("config_location")
.short('C')
.long("config")
.action(ArgAction::Set)
.value_name("CONFIG PATH")
.help("Sets the location of the config file.")
.long_help("Sets the location of the config file. Expects a config file in the TOML format. If it doesn't exist, one is created.")
.value_hint(ValueHint::AnyPath);
let default_time_value = Arg::new("default_time_value")
let default_time_value = Arg::new("default_time_value")
.short('t')
.long("default_time_value")
.action(ArgAction::Set)
.value_name("TIME")
.help("Default time value for graphs.")
.long_help(
"Default time value for graphs. Takes a number in milliseconds or a human duration (e.g. 60s). The minimum time is 30s, and the default is 60s.",
)
.help_heading(HEADING);
.long_help("Default time value for graphs. Takes a number in milliseconds or a human duration (e.g. 60s). The minimum time is 30s, and the default is 60s.");
// TODO: Charts are broken in the manpage
let default_widget_count = Arg::new("default_widget_count")
.long("default_widget_count")
.action(ArgAction::Set)
.requires_all(["default_widget_type"])
.value_name("INT")
.help("Sets the n'th selected widget type as the default.")
.long_help(
"\
// TODO: Charts are broken in the manpage
let default_widget_count = Arg::new("default_widget_count")
.long("default_widget_count")
.action(ArgAction::Set)
.requires_all(["default_widget_type"])
.value_name("INT")
.help("Sets the n'th selected widget type as the default.")
.long_help(
"\
Sets the n'th selected widget type to use as the default widget.
Requires 'default_widget_type' to also be set, and defaults to 1.
@ -222,94 +211,85 @@ And we set our default widget type to 'CPU'. If we set
the default widget. If we set '--default_widget_count 3', it would
use CPU (3) as the default instead.
",
)
.help_heading(HEADING);
);
let default_widget_type = Arg::new("default_widget_type")
.long("default_widget_type")
.action(ArgAction::Set)
.value_name("WIDGET TYPE")
.help("Sets the default widget type, use --help for info.")
.long_help(DEFAULT_WIDGET_TYPE_STR)
.help_heading(HEADING);
let default_widget_type = Arg::new("default_widget_type")
.long("default_widget_type")
.action(ArgAction::Set)
.value_name("WIDGET TYPE")
.help("Sets the default widget type, use --help for info.")
.long_help(DEFAULT_WIDGET_TYPE_STR);
let expanded_on_startup = Arg::new("expanded_on_startup")
.short('e')
.long("expanded")
.action(ArgAction::SetTrue)
.help("Expand the default widget upon starting the app.")
.long_help("Expand the default widget upon starting the app. Same as pressing \"e\" inside the app. Use with \"default_widget_type\" and \"default_widget_count\" to select desired expanded widget. This flag has no effect in basic mode (--basic)")
.help_heading(HEADING);
let expanded_on_startup = Arg::new("expanded_on_startup")
.short('e')
.long("expanded")
.action(ArgAction::SetTrue)
.help("Expand the default widget upon starting the app.")
.long_help("Expand the default widget upon starting the app. Same as pressing \"e\" inside the app. Use with \"default_widget_type\" and \"default_widget_count\" to select desired expanded widget. This flag has no effect in basic mode (--basic).");
let rate = Arg::new("rate")
.short('r')
.long("rate")
.action(ArgAction::Set)
.value_name("TIME")
.help("Sets the data refresh rate.")
.long_help("Sets the data refresh rate. Takes a number in milliseconds or a human duration (e.g. 5s). The minimum is 250ms, and defaults to 1000ms. Smaller values may take more computer resources.")
.help_heading(HEADING);
let rate = Arg::new("rate")
.short('r')
.long("rate")
.action(ArgAction::Set)
.value_name("TIME")
.help("Sets the data refresh rate.")
.long_help("Sets the data refresh rate. Takes a number in milliseconds or a human duration (e.g. 5s). The minimum is 250ms, and defaults to 1000ms. Smaller values may take more computer resources.");
let time_delta = Arg::new("time_delta")
.short('d')
.long("time_delta")
.action(ArgAction::Set)
.value_name("TIME")
.help("The amount of time changed upon zooming.")
.long_help("The amount of time changed when zooming in/out. Takes a number in milliseconds or a human duration (e.g. 30s). The minimum is 1s, and defaults to 15s.")
.help_heading(HEADING);
let time_delta = Arg::new("time_delta")
.short('d')
.long("time_delta")
.action(ArgAction::Set)
.value_name("TIME")
.help("The amount of time changed upon zooming.")
.long_help("The amount of time changed when zooming in/out. Takes a number in milliseconds or a human duration (e.g. 30s). The minimum is 1s, and defaults to 15s.");
let retention = Arg::new("retention")
.long("retention")
.action(ArgAction::Set)
.value_name("TIME")
.help("The timespan of data stored.")
.long_help("How much data is stored at once in terms of time. Takes a number in milliseconds or a human duration (e.g. 20m), with a minimum of 1 minute. Note higher values will take up more memory. Defaults to 10 minutes.")
.help_heading(HEADING);
let retention = Arg::new("retention")
.long("retention")
.action(ArgAction::Set)
.value_name("TIME")
.help("The timespan of data stored.")
.long_help("How much data is stored at once in terms of time. Takes a number in milliseconds or a human duration (e.g. 20m), with a minimum of 1 minute. Note higher values will take up more memory. Defaults to 10 minutes.");
let mut args = [
autohide_time,
basic,
disable_click,
dot_marker,
hide_table_gap,
hide_time,
left_legend,
show_table_scroll_position,
config_location,
default_time_value,
default_widget_count,
default_widget_type,
expanded_on_startup,
rate,
time_delta,
retention,
];
args.sort_unstable();
cmd.args(args![
autohide_time,
basic,
disable_click,
dot_marker,
hide_table_gap,
hide_time,
left_legend,
show_table_scroll_position,
config_location,
default_time_value,
default_widget_count,
default_widget_type,
expanded_on_startup,
rate,
time_delta,
retention,
])
}
self.args(args)
}
fn style_args(cmd: Command) -> Command {
let cmd = cmd.next_help_heading("Style Options");
fn style_args(self) -> Command {
const HEADING: &str = "Style Options";
// TODO: File an issue with manpage, it cannot render charts correctly.
let color = Arg::new("color")
.long("color")
.action(ArgAction::Set)
.value_name("COLOR SCHEME")
.value_parser(PossibleValuesParser::new([
"default",
"default-light",
"gruvbox",
"gruvbox-light",
"nord",
"nord-light",
]))
.hide_possible_values(true)
.help("Use a color scheme, use --help for info.")
.long_help(
"\
// TODO: File an issue with manpage, it cannot render charts correctly.
let color = Arg::new("color")
.long("color")
.action(ArgAction::Set)
.value_name("COLOR SCHEME")
.value_parser(PossibleValuesParser::new([
"default",
"default-light",
"gruvbox",
"gruvbox-light",
"nord",
"nord-light",
]))
.hide_possible_values(true)
.help("Use a color scheme, use --help for info.")
.long_help(
"\
Use a pre-defined color scheme. Currently supported values are:
+------------------------------------------------------------+
| default |
@ -326,285 +306,241 @@ Use a pre-defined color scheme. Currently supported values are:
+------------------------------------------------------------+
Defaults to \"default\".
",
)
.help_heading(HEADING);
);
self.arg(color)
cmd.arg(color)
}
fn temperature_args(cmd: Command) -> Command {
let cmd = cmd.next_help_heading("Temperature Options");
let celsius = Arg::new("celsius")
.short('c')
.long("celsius")
.action(ArgAction::SetTrue)
.help("Use Celsius as the temperature unit.")
.long_help("Use Celsius as the temperature unit. This is the default option.");
let fahrenheit = Arg::new("fahrenheit")
.short('f')
.long("fahrenheit")
.action(ArgAction::SetTrue)
.help("Use Fahrenheit as the temperature unit.");
let kelvin = Arg::new("kelvin")
.short('k')
.long("kelvin")
.action(ArgAction::SetTrue)
.help("Use Kelvin as the temperature unit.");
let temperature_group = ArgGroup::new("TEMPERATURE_TYPE").args([
celsius.get_id(),
fahrenheit.get_id(),
kelvin.get_id(),
]);
cmd.args(args![celsius, fahrenheit, kelvin])
.group(temperature_group)
}
fn process_args(cmd: Command) -> Command {
let cmd = cmd.next_help_heading("Process Options");
let case_sensitive = Arg::new("case_sensitive")
.short('S')
.long("case_sensitive")
.action(ArgAction::SetTrue)
.help("Enables case sensitivity by default.")
.long_help("When searching for a process, enables case sensitivity by default.");
let current_usage = Arg::new("current_usage")
.short('u')
.long("current_usage")
.action(ArgAction::SetTrue)
.help("Sets process CPU% to be based on current CPU%.")
.long_help("Sets process CPU% usage to be based on the current system CPU% usage rather than total CPU usage.");
let unnormalized_cpu = Arg::new("unnormalized_cpu")
.short('n')
.long("unnormalized_cpu")
.action(ArgAction::SetTrue)
.help("Show process CPU% usage without normalizing over the number of cores.")
.long_help("Shows all process CPU% usage without averaging over the number of CPU cores in the system.");
let group_processes = Arg::new("group_processes")
.short('g')
.long("group_processes")
.action(ArgAction::SetTrue)
.help("Groups processes with the same name by default.")
.long_help("Groups processes with the same name by default.");
let process_command = Arg::new("process_command")
.long("process_command")
.action(ArgAction::SetTrue)
.help("Show processes as their commands by default.")
.long_help("Show processes as their commands by default in the process widget.");
let regex = Arg::new("regex")
.short('R')
.long("regex")
.action(ArgAction::SetTrue)
.help("Enables regex by default.")
.long_help("When searching for a process, enables regex by default.");
let disable_advanced_kill = Arg::new("disable_advanced_kill")
.long("disable_advanced_kill")
.action(ArgAction::SetTrue)
.help("Hides advanced process killing.")
.long_help("Hides advanced options to stop a process on Unix-like systems. The only option shown is 15 (TERM).");
let whole_word = Arg::new("whole_word")
.short('W')
.long("whole_word")
.action(ArgAction::SetTrue)
.help("Enables whole-word matching by default.")
.long_help(
"When searching for a process, return results that match the entire query by default.",
);
let tree = Arg::new("tree")
.short('T')
.long("tree")
.action(ArgAction::SetTrue)
.help("Defaults the process widget be in tree mode.")
.long_help("Defaults to showing the process widget in tree mode.");
let args = args![
case_sensitive,
current_usage,
unnormalized_cpu,
group_processes,
process_command,
regex,
whole_word,
disable_advanced_kill,
tree,
];
cmd.args(args)
}
fn cpu_args(cmd: Command) -> Command {
let cmd = cmd.next_help_heading("CPU Options");
let hide_avg_cpu = Arg::new("hide_avg_cpu")
.short('a')
.long("hide_avg_cpu")
.action(ArgAction::SetTrue)
.help("Hides the average CPU usage.")
.long_help("Hides the average CPU usage from being shown.");
// let default_avg_cpu = Arg::new("");
cmd.args(args![hide_avg_cpu])
}
fn mem_args(cmd: Command) -> Command {
let cmd = cmd.next_help_heading("Memory Options");
let mem_as_value = Arg::new("mem_as_value")
.long("mem_as_value")
.action(ArgAction::SetTrue)
.help("Defaults to showing process memory usage by value.")
.long_help("Defaults to showing process memory usage by value. Otherwise, it defaults to showing it by percentage.");
#[cfg(not(target_os = "windows"))]
{
let enable_cache_memory = Arg::new("enable_cache_memory")
.long("enable_cache_memory")
.action(ArgAction::SetTrue)
.help("Enable collecting and displaying cache and buffer memory.");
cmd.args(args![mem_as_value, enable_cache_memory])
}
fn temperature_args(self) -> Command {
const HEADING: &str = "Temperature Options";
let celsius = Arg::new("celsius")
.short('c')
.long("celsius")
.action(ArgAction::SetTrue)
.help("Use Celsius as the temperature unit.")
.long_help("Use Celsius as the temperature unit. This is the default option.")
.help_heading(HEADING);
let fahrenheit = Arg::new("fahrenheit")
.short('f')
.long("fahrenheit")
.action(ArgAction::SetTrue)
.help("Use Fahrenheit as the temperature unit.")
.help_heading(HEADING);
let kelvin = Arg::new("kelvin")
.short('k')
.long("kelvin")
.action(ArgAction::SetTrue)
.help("Use Kelvin as the temperature unit.")
.help_heading(HEADING);
let temperature_group = ArgGroup::new("TEMPERATURE_TYPE").args([
celsius.get_id(),
fahrenheit.get_id(),
kelvin.get_id(),
]);
let args = [celsius, fahrenheit, kelvin];
self.args(args).group(temperature_group)
#[cfg(target_os = "windows")]
{
cmd.arg(mem_as_value)
}
}
fn process_args(self) -> Command {
const HEADING: &str = "Process Options";
fn network_args(cmd: Command) -> Command {
let cmd = cmd.next_help_heading("Network Options");
let case_sensitive = Arg::new("case_sensitive")
.short('S')
.long("case_sensitive")
.action(ArgAction::SetTrue)
.help("Enables case sensitivity by default.")
.long_help("When searching for a process, enables case sensitivity by default.")
.help_heading(HEADING);
let use_old_network_legend = Arg::new("use_old_network_legend")
.long("use_old_network_legend")
.action(ArgAction::SetTrue)
.help("DEPRECATED - uses a separate network legend.")
.long_help("DEPRECATED - uses an older (pre-0.4), separate network widget legend. This display is not tested anymore and may be broken.");
let current_usage = Arg::new("current_usage")
.short('u')
.long("current_usage")
.action(ArgAction::SetTrue)
.help("Sets process CPU% to be based on current CPU%.")
.long_help("Sets process CPU% usage to be based on the current system CPU% usage rather than total CPU usage.")
.help_heading(HEADING);
let network_use_bytes = Arg::new("network_use_bytes")
.long("network_use_bytes")
.action(ArgAction::SetTrue)
.help("Displays the network widget using bytes.")
.long_help("Displays the network widget using bytes. Defaults to bits.");
let unnormalized_cpu = Arg::new("unnormalized_cpu")
.short('n')
.long("unnormalized_cpu")
.action(ArgAction::SetTrue)
.help("Show process CPU% usage without normalizing over the number of cores.")
.long_help(
"Shows all process CPU% usage without averaging over the number of CPU cores in the system.",
)
.help_heading(HEADING);
let network_use_log = Arg::new("network_use_log")
.long("network_use_log")
.action(ArgAction::SetTrue)
.help("Displays the network widget with a log scale.")
.long_help("Displays the network widget with a log scale. Defaults to a non-log scale.");
let group_processes = Arg::new("group_processes")
.short('g')
.long("group_processes")
.action(ArgAction::SetTrue)
.help("Groups processes with the same name by default.")
.long_help("Groups processes with the same name by default.")
.help_heading(HEADING);
let network_use_binary_prefix = Arg::new("network_use_binary_prefix")
.long("network_use_binary_prefix")
.action(ArgAction::SetTrue)
.help("Displays the network widget with binary prefixes.")
.long_help("Displays the network widget with binary prefixes (i.e. kibibits, mebibits) rather than a decimal prefix (i.e. kilobits, megabits). Defaults to decimal prefixes.");
let process_command = Arg::new("process_command")
.long("process_command")
.action(ArgAction::SetTrue)
.help("Show processes as their commands by default.")
.long_help("Show processes as their commands by default in the process widget.")
.help_heading(HEADING);
cmd.args(args![
use_old_network_legend,
network_use_bytes,
network_use_log,
network_use_binary_prefix,
])
}
let regex = Arg::new("regex")
.short('R')
.long("regex")
.action(ArgAction::SetTrue)
.help("Enables regex by default.")
.long_help("When searching for a process, enables regex by default.")
.help_heading(HEADING);
#[cfg(feature = "battery")]
fn battery_args(cmd: Command) -> Command {
let cmd = cmd.next_help_heading("Battery Options");
let disable_advanced_kill = Arg::new("disable_advanced_kill")
.long("disable_advanced_kill")
.action(ArgAction::SetTrue)
.help("Hides advanced process killing.")
.long_help("Hides advanced options to stop a process on Unix-like systems. The only option shown is 15 (TERM).")
.help_heading(HEADING);
let battery = Arg::new("battery")
.long("battery")
.action(ArgAction::SetTrue)
.help("Shows the battery widget.")
.long_help(
"Shows the battery widget in default or basic mode. No effect on custom layouts.",
);
let whole_word = Arg::new("whole_word")
.short('W')
.long("whole_word")
.action(ArgAction::SetTrue)
.help("Enables whole-word matching by default.")
.long_help(
"When searching for a process, return results that match the entire query by default.",
)
.help_heading(HEADING);
cmd.arg(battery)
}
let tree = Arg::new("tree")
.short('T')
.long("tree")
.action(ArgAction::SetTrue)
.help("Defaults the process widget be in tree mode.")
.long_help("Defaults to showing the process widget in tree mode.")
.help_heading(HEADING);
#[cfg(feature = "gpu")]
fn gpu_args(cmd: Command) -> Command {
let cmd = cmd.next_help_heading("GPU Options");
let mut args = [
case_sensitive,
current_usage,
unnormalized_cpu,
group_processes,
process_command,
regex,
whole_word,
disable_advanced_kill,
tree,
];
args.sort_unstable();
let enable_gpu = Arg::new("enable_gpu")
.long("enable_gpu")
.action(ArgAction::SetTrue)
.help("Enable collecting and displaying GPU usage.");
self.args(args)
}
cmd.arg(enable_gpu)
}
fn cpu_args(self) -> Command {
const HEADING: &str = "CPU Options";
fn other_args(cmd: Command) -> Command {
let cmd = cmd.next_help_heading("Other Options");
let hide_avg_cpu = Arg::new("hide_avg_cpu")
.short('a')
.long("hide_avg_cpu")
.action(ArgAction::SetTrue)
.help("Hides the average CPU usage.")
.long_help("Hides the average CPU usage from being shown.")
.help_heading(HEADING);
let help = Arg::new("help")
.short('h')
.long("help")
.action(ArgAction::Help)
.help("Prints help (see more info with '--help').");
self.arg(hide_avg_cpu)
}
let version = Arg::new("version")
.short('V')
.long("version")
.action(ArgAction::Version)
.help("Prints version information.");
fn mem_args(self) -> Command {
const HEADING: &str = "Memory Options";
let mem_as_value = Arg::new("mem_as_value")
.long("mem_as_value")
.action(ArgAction::SetTrue)
.help("Defaults to showing process memory usage by value.")
.long_help("Defaults to showing process memory usage by value. Otherwise, it defaults to showing it by percentage.")
.help_heading(HEADING);
#[cfg(not(target_os = "windows"))]
{
let enable_cache_memory = Arg::new("enable_cache_memory")
.long("enable_cache_memory")
.action(ArgAction::SetTrue)
.help("Enable collecting and displaying cache and buffer memory.")
.help_heading(HEADING);
self.args([mem_as_value, enable_cache_memory])
}
#[cfg(target_os = "windows")]
{
self.arg(mem_as_value)
}
}
fn network_args(self) -> Command {
const HEADING: &str = "Network Options";
let use_old_network_legend = Arg::new("use_old_network_legend")
.long("use_old_network_legend")
.action(ArgAction::SetTrue)
.help("DEPRECATED - uses a separate network legend.")
.long_help(
"DEPRECATED - uses an older (pre-0.4), separate network widget legend. This display is not \
tested anymore and could be broken.",
)
.help_heading(HEADING);
let network_use_bytes = Arg::new("network_use_bytes")
.long("network_use_bytes")
.action(ArgAction::SetTrue)
.help("Displays the network widget using bytes.")
.long_help("Displays the network widget using bytes. Defaults to bits.")
.help_heading(HEADING);
let network_use_log = Arg::new("network_use_log")
.long("network_use_log")
.action(ArgAction::SetTrue)
.help("Displays the network widget with a log scale.")
.long_help("Displays the network widget with a log scale. Defaults to a non-log scale.")
.help_heading(HEADING);
let network_use_binary_prefix = Arg::new("network_use_binary_prefix")
.long("network_use_binary_prefix")
.action(ArgAction::SetTrue)
.help("Displays the network widget with binary prefixes.")
.long_help(
"Displays the network widget with binary prefixes (i.e. kibibits, mebibits) rather than a decimal prefix (i.e. kilobits, megabits). Defaults to decimal prefixes.",
)
.help_heading(HEADING);
let mut args = [
use_old_network_legend,
network_use_bytes,
network_use_log,
network_use_binary_prefix,
];
args.sort_unstable();
self.args(args)
}
fn battery_args(self) -> Command {
#[cfg(feature = "battery")]
{
let battery = Arg::new("battery")
.long("battery")
.action(ArgAction::SetTrue)
.help("Shows the battery widget.")
.long_help(
"Shows the battery widget in default or basic mode. No effect on custom layouts.",
)
.help_heading("Battery Options");
self.arg(battery)
}
#[cfg(not(feature = "battery"))]
{
self
}
}
fn gpu_args(self) -> Command {
#[cfg(feature = "gpu")]
{
let enable_gpu = Arg::new("enable_gpu")
.long("enable_gpu")
.action(ArgAction::SetTrue)
.help("Enable collecting and displaying GPU usage.")
.help_heading("GPU Options");
self.arg(enable_gpu)
}
#[cfg(not(feature = "gpu"))]
{
self
}
}
fn other(self) -> Command {
const HEADING: &str = "Other Options";
let version = Arg::new("version")
.short('V')
.long("version")
.action(ArgAction::Version)
.help("Prints version information.")
.help_heading(HEADING);
let help = Arg::new("help")
.short('h')
.long("help")
.action(ArgAction::Help)
.help("Prints help (see more with '--help').")
.help_heading(HEADING);
self.args([version, help])
}
cmd.args([help, version])
}
pub fn build_app() -> Command {
@ -615,7 +551,7 @@ pub fn build_app() -> Command {
None => crate_version!(),
};
Command::new(crate_name!())
let cmd = Command::new(crate_name!())
.author(crate_authors!())
.about(crate_description!())
.disable_help_flag(true)
@ -623,17 +559,24 @@ pub fn build_app() -> Command {
.color(ColorChoice::Auto)
.help_template(TEMPLATE)
.override_usage(USAGE)
.version(VERSION)
.general_args()
.style_args()
.temperature_args()
.process_args()
.cpu_args()
.mem_args()
.network_args()
.battery_args()
.gpu_args()
.other()
.version(VERSION);
[
general_args,
process_args,
temperature_args,
cpu_args,
mem_args,
network_args,
#[cfg(feature = "battery")]
battery_args,
#[cfg(feature = "gpu")]
gpu_args,
style_args,
other_args,
]
.into_iter()
.fold(cmd, |c, f| f(c))
}
#[cfg(test)]
@ -646,7 +589,7 @@ mod test {
}
#[test]
fn help_heading_no_default() {
fn no_default_help_heading() {
let mut app = build_app();
let help_str = app.render_help();