refactor: use struct for args instead of builder interface (#1472)

* start moving args

* tmp

* refactor config

* port over ags

* update changelog
This commit is contained in:
Clement Tsang 2024-05-27 01:16:37 -04:00 committed by GitHub
parent 71f6136a1e
commit ee2e1fee1c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
20 changed files with 769 additions and 736 deletions

View file

@ -21,9 +21,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- [#1436](https://github.com/ClementTsang/bottom/pull/1436): Use actual "swap" value for Windows.
- [#1441](https://github.com/ClementTsang/bottom/pull/1441): The following arguments have changed names:
- `--left_legend/-l` is now `--cpu_left_legend`.
- [#1441](https://github.com/ClementTsang/bottom/pull/1441): The following config arguments have changed names:
- [#1441](https://github.com/ClementTsang/bottom/pull/1441): The following config fields have changed names:
- `expanded_on_startup` is now `expanded`.
- `left_legend` is now `cpu_left_legend`.
- [#1472](https://github.com/ClementTsang/bottom/pull/1472): The following arguments have changed names:
- `mem_as_value` is now `process_memory_as_value`.
- [#1472](https://github.com/ClementTsang/bottom/pull/1472): The following config fields have changed names:
- `mem_as_value` is now `process_memory_as_value`.
### Bug Fixes

23
Cargo.lock generated
View file

@ -258,6 +258,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90bc066a67923782aa8515dbaea16946c5bcc5addbd668bb80af688e53e548a0"
dependencies = [
"clap_builder",
"clap_derive",
]
[[package]]
@ -302,6 +303,18 @@ dependencies = [
"clap_complete",
]
[[package]]
name = "clap_derive"
version = "4.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "528131438037fd55894f62d6e9f068b8f45ac57ffa77517819645d10aed04f64"
dependencies = [
"heck 0.5.0",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "clap_lex"
version = "0.7.0"
@ -514,7 +527,7 @@ version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5ffccbb6966c05b32ef8fbac435df276c4ae4d3dc55a8cd0eb9745e6c12f546a"
dependencies = [
"heck",
"heck 0.4.1",
"proc-macro2",
"quote",
"syn",
@ -604,6 +617,12 @@ version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
[[package]]
name = "heck"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
[[package]]
name = "humantime"
version = "2.1.0"
@ -1333,7 +1352,7 @@ version = "0.26.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c6cf59daf282c0a494ba14fd21610a0325f9f90ec9d1231dea26bcb1d696c946"
dependencies = [
"heck",
"heck 0.4.1",
"proc-macro2",
"quote",
"rustversion",

View file

@ -19,7 +19,9 @@ exclude = [
"desktop/",
"docs/",
"sample_configs/",
"schema",
"scripts/",
"wix/",
".all-contributorsrc",
".cirrus.yml",
".gitignore",
@ -29,6 +31,9 @@ exclude = [
"codecov.yml",
"CONTRIBUTING.md",
"Cross.toml",
"debug.log",
"flamegraph.svg",
"perf.data",
"rustfmt.toml",
]
rust-version = "1.74.0" # The oldest version I've tested that should still build - note this is not an official MSRV!
@ -75,7 +80,7 @@ default = ["deploy"]
anyhow = "1.0.86"
backtrace = "0.3.71"
cfg-if = "1.0.0"
clap = { version = "4.5.4", features = ["default", "cargo", "wrap_help"] }
clap = { version = "4.5.4", features = ["default", "cargo", "wrap_help", "derive"] }
concat-string = "1.0.1"
crossterm = "0.27.0"
ctrlc = { version = "3.4.4", features = ["termination"] }
@ -135,7 +140,7 @@ predicates = "3.1.0"
portable-pty = "0.8.1"
[build-dependencies]
clap = { version = "4.5.4", features = ["default", "cargo", "wrap_help"] }
clap = { version = "4.5.4", features = ["default", "cargo", "wrap_help", "derive"] }
clap_complete = "4.5.2"
clap_complete_nushell = "4.5.1"
clap_complete_fig = "4.5.0"

View file

@ -7,12 +7,12 @@ use std::{
path::{Path, PathBuf},
};
use clap::Command;
use clap::{Command, CommandFactory};
use clap_complete::{generate_to, shells::Shell, Generator};
use clap_complete_fig::Fig;
use clap_complete_nushell::Nushell;
use crate::args::build_app;
use crate::args::BottomArgs;
fn create_dir(dir: &Path) -> io::Result<()> {
let res = fs::create_dir_all(dir);
@ -48,7 +48,7 @@ fn btm_generate() -> io::Result<()> {
create_dir(&manpage_out_dir)?;
// Generate completions
let mut app = build_app();
let mut app = BottomArgs::command();
generate_completions(Shell::Bash, &mut app, &completion_out_dir)?;
generate_completions(Shell::Zsh, &mut app, &completion_out_dir)?;
generate_completions(Shell::Fish, &mut app, &completion_out_dir)?;

View file

@ -25,18 +25,18 @@ see information on these options by running `btm -h`, or run `btm --help` to dis
## Process Options
| Option | Behaviour |
| ------------------------- | -------------------------------------------------------------------------------------- |
| `-S, --case_sensitive` | Enables case sensitivity by default. |
| `-u, --current_usage` | Calculates process CPU usage as a percentage of current usage rather than total usage. |
| `--disable_advanced_kill` | Hides additional stopping options Unix-like systems. |
| `-g, --group_processes` | Groups processes with the same name by default. |
| `--mem_as_value` | Defaults to showing process memory usage by value. |
| `--process_command` | Shows the full command name instead of the process name by default. |
| `-R, --regex` | Enables regex by default while searching. |
| `-T, --tree` | Makes the process widget use tree mode by default. |
| `-n, --unnormalized_cpu` | Show process CPU% usage without averaging over the number of CPU cores. |
| `-W, --whole_word` | Enables whole-word matching by default while searching. |
| Option | Behaviour |
| --------------------------- | -------------------------------------------------------------------------------------- |
| `-S, --case_sensitive` | Enables case sensitivity by default. |
| `-u, --current_usage` | Calculates process CPU usage as a percentage of current usage rather than total usage. |
| `--disable_advanced_kill` | Hides additional stopping options Unix-like systems. |
| `-g, --group_processes` | Groups processes with the same name by default. |
| `--process_memory_as_value` | Defaults to showing process memory usage by value. |
| `--process_command` | Shows the full command name instead of the process name by default. |
| `-R, --regex` | Enables regex by default while searching. |
| `-T, --tree` | Makes the process widget use tree mode by default. |
| `-n, --unnormalized_cpu` | Show process CPU% usage without averaging over the number of CPU cores. |
| `-W, --whole_word` | Enables whole-word matching by default while searching. |
## Temperature Options

View file

@ -18,7 +18,7 @@ each time:
| ---------------------------- | ------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------- |
| `hide_avg_cpu` | Boolean | Hides the average CPU usage. |
| `dot_marker` | Boolean | Uses a dot marker for graphs. |
| `cpu_left_legend` | Boolean | Puts the CPU chart legend to the left side. |
| `cpu_left_legend` | Boolean | Puts the CPU chart legend to the left side. |
| `current_usage` | Boolean | Sets process CPU% to be based on current CPU%. |
| `group_processes` | Boolean | Groups processes with the same name by default. |
| `case_sensitive` | Boolean | Enables case sensitivity by default. |
@ -37,7 +37,7 @@ each time:
| `disable_click` | Boolean | Disables mouse clicks. |
| `color` | String (one of ["default", "default-light", "gruvbox", "gruvbox-light", "nord", "nord-light"]) | Use a color scheme, use --help for supported values. |
| `enable_cache_memory` | Boolean | Enable cache and buffer memory stats (not available on Windows). |
| `mem_as_value` | Boolean | Defaults to showing process memory usage by value. |
| `process_memory_as_value` | Boolean | Defaults to showing process memory usage by value. |
| `tree` | Boolean | Defaults to showing the process widget in tree mode. |
| `show_table_scroll_position` | Boolean | Shows the scroll position tracker in table widgets. |
| `process_command` | Boolean | Show processes as their commands by default. |
@ -48,6 +48,6 @@ each time:
| `enable_gpu` | Boolean | Shows the GPU widgets. |
| `retention` | String (human readable time, such as "10m", "1h", etc.) | How much data is stored at once in terms of time. |
| `unnormalized_cpu` | Boolean | Show process CPU% without normalizing over the number of cores. |
| `expanded` | Boolean | Expand the default widget upon starting the app. |
| `expanded` | Boolean | Expand the default widget upon starting the app. |
| `memory_legend` | String (one of ["none", "top-left", "top", "top-right", "left", "right", "bottom-left", "bottom", "bottom-right"]) | Where to place the legend for the memory widget. |
| `network_legend` | String (one of ["none", "top-left", "top", "top-right", "left", "right", "bottom-left", "bottom", "bottom-right"]) | Where to place the legend for the network widget. |

View file

@ -58,7 +58,7 @@
# Built-in themes. Valid values are "default", "default-light", "gruvbox", "gruvbox-light", "nord", "nord-light"
#color = "default"
# Show memory values in the processes widget as values by default
#mem_as_value = false
#process_memory_as_value = false
# Show tree mode by default in the processes widget.
#tree = false
# Shows an indicator in table widgets tracking where in the list you are.

View file

@ -184,7 +184,7 @@
"description": "Built-in themes",
"type": "string"
},
"mem_as_value": {
"process_memory_as_value": {
"default": false,
"description": "Show memory values in the processes widget as values by default",
"type": "boolean"

View file

@ -17,11 +17,10 @@ use bottom::{
args,
canvas::{self, styling::CanvasStyling},
check_if_terminal, cleanup_terminal, create_collection_thread, create_input_thread,
create_or_get_config,
data_conversion::*,
handle_key_event_or_break, handle_mouse_event,
options::{get_color_scheme, get_widget_layout, init_app},
panic_hook, read_config, try_drawing, update_data, BottomEvent,
get_or_create_config, handle_key_event_or_break, handle_mouse_event,
options::{get_color_scheme, init_app},
panic_hook, try_drawing, update_data, BottomEvent,
};
use crossterm::{
event::{EnableBracketedPaste, EnableMouseCapture},
@ -37,7 +36,7 @@ use tui::{backend::CrosstermBackend, Terminal};
fn main() -> Result<()> {
// let _profiler = dhat::Profiler::new_heap();
let matches = args::get_matches();
let args = args::get_args();
#[cfg(feature = "logging")]
{
@ -50,34 +49,17 @@ fn main() -> Result<()> {
}
// Read from config file.
let config = {
let config_path = read_config(matches.get_one::<String>("config_location"))
.context("Unable to access the given config file location.")?;
create_or_get_config(&config_path)
.context("Unable to properly parse or create the config file.")?
};
// Get widget layout separately
let (widget_layout, default_widget_id, default_widget_type_option) =
get_widget_layout(&matches, &config)
.context("Found an issue while trying to build the widget layout.")?;
let config = get_or_create_config(args.general.config_location.as_deref())
.context("Unable to parse or create the config file.")?;
// FIXME: Should move this into build app or config
let styling = {
let colour_scheme = get_color_scheme(&matches, &config)?;
let colour_scheme = get_color_scheme(&args, &config)?;
CanvasStyling::new(colour_scheme, &config)?
};
// Create an "app" struct, which will control most of the program and store settings/state
let mut app = init_app(
matches,
config,
&widget_layout,
default_widget_id,
&default_widget_type_option,
&styling,
)?;
let (mut app, widget_layout) = init_app(args, config, &styling)?;
// Create painter and set colours.
let mut painter = canvas::Painter::init(widget_layout, styling)?;

View file

@ -5,7 +5,7 @@ use colour_utils::*;
use tui::style::{Color, Style};
use super::ColourScheme;
pub use crate::options::Config;
pub use crate::options::ConfigV1;
use crate::{constants::*, options::colours::ConfigColours, utils::error};
pub struct CanvasStyling {
@ -124,7 +124,7 @@ macro_rules! try_set_colour_list {
}
impl CanvasStyling {
pub fn new(colour_scheme: ColourScheme, config: &Config) -> anyhow::Result<Self> {
pub fn new(colour_scheme: ColourScheme, config: &ConfigV1) -> anyhow::Result<Self> {
let mut canvas_colours = Self::default();
match colour_scheme {
@ -236,7 +236,7 @@ mod test {
use tui::style::{Color, Style};
use super::{CanvasStyling, ColourScheme};
use crate::options::Config;
use crate::options::ConfigV1;
#[test]
fn default_selected_colour_works() {
@ -282,7 +282,7 @@ mod test {
#[test]
fn built_in_colour_schemes_work() {
let config = Config::default();
let config = ConfigV1::default();
CanvasStyling::new(ColourScheme::Default, &config).unwrap();
CanvasStyling::new(ColourScheme::DefaultLight, &config).unwrap();
CanvasStyling::new(ColourScheme::Gruvbox, &config).unwrap();

View file

@ -576,7 +576,7 @@ pub const CONFIG_TEXT: &str = r#"# This is a default config file for bottom. Al
# Built-in themes. Valid values are "default", "default-light", "gruvbox", "gruvbox-light", "nord", "nord-light"
#color = "default"
# Show memory values in the processes widget as values by default
#mem_as_value = false
#process_memory_as_value = false
# Show tree mode by default in the processes widget.
#tree = false
# Shows an indicator in table widgets tracking where in the list you are.

View file

@ -13,6 +13,8 @@ cfg_if::cfg_if! {
}
}
use std::str::FromStr;
use crate::app::filter::Filter;
#[derive(Default, Debug, Clone)]
@ -29,6 +31,21 @@ pub enum TemperatureType {
Fahrenheit,
}
impl FromStr for TemperatureType {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"fahrenheit" | "f" => Ok(TemperatureType::Fahrenheit),
"kelvin" | "k" => Ok(TemperatureType::Kelvin),
"celsius" | "c" => Ok(TemperatureType::Celsius),
_ => Err(format!(
"\"{s}\" is an invalid temperature type, use \"<kelvin|k|celsius|c|fahrenheit|f>\"."
)),
}
}
}
impl TemperatureType {
/// Given a temperature in Celsius, covert it if necessary for a different unit.
pub fn convert_temp_unit(&self, temp_celsius: f32) -> f32 {

View file

@ -35,7 +35,7 @@ use std::{
fs,
io::{stderr, stdout, Write},
panic::PanicInfo,
path::PathBuf,
path::{Path, PathBuf},
sync::{
mpsc::{Receiver, Sender},
Arc, Condvar, Mutex,
@ -61,7 +61,7 @@ use crossterm::{
};
use data_conversion::*;
pub use options::args;
use options::Config;
use options::ConfigV1;
use utils::error;
#[allow(unused_imports)]
pub use utils::logging::*;
@ -202,9 +202,9 @@ pub fn handle_key_event_or_break(
false
}
pub fn read_config(config_location: Option<&String>) -> error::Result<Option<PathBuf>> {
let config_path = if let Some(conf_loc) = config_location {
Some(PathBuf::from(conf_loc.as_str()))
pub fn get_config_path(override_config_path: Option<&Path>) -> Option<PathBuf> {
if let Some(conf_loc) = override_config_path {
Some(conf_loc.to_path_buf())
} else if cfg!(target_os = "windows") {
if let Some(home_path) = dirs::config_dir() {
let mut path = home_path;
@ -232,13 +232,13 @@ pub fn read_config(config_location: Option<&String>) -> error::Result<Option<Pat
}
} else {
None
};
Ok(config_path)
}
}
pub fn create_or_get_config(config_path: &Option<PathBuf>) -> error::Result<Config> {
if let Some(path) = config_path {
pub fn get_or_create_config(override_config_path: Option<&Path>) -> error::Result<ConfigV1> {
let config_path = get_config_path(override_config_path);
if let Some(path) = &config_path {
if let Ok(config_string) = fs::read_to_string(path) {
Ok(toml_edit::de::from_str(config_string.as_str())?)
} else {
@ -247,11 +247,11 @@ pub fn create_or_get_config(config_path: &Option<PathBuf>) -> error::Result<Conf
}
fs::File::create(path)?.write_all(CONFIG_TEXT.as_bytes())?;
Ok(Config::default())
Ok(ConfigV1::default())
}
} else {
// Don't write...
Ok(Config::default())
// If we somehow don't have any config path, then just assume the default config but don't write to any file.
Ok(ConfigV1::default())
}
}

View file

@ -13,16 +13,18 @@ use std::{
};
use anyhow::{Context, Result};
use clap::ArgMatches;
pub use colours::ConfigColours;
pub use config::Config;
pub use config::ConfigV1;
use hashbrown::{HashMap, HashSet};
use indexmap::IndexSet;
use regex::Regex;
#[cfg(feature = "battery")]
use starship_battery::Manager;
use self::config::{layout::Row, IgnoreList, StringOrNum};
use self::{
args::BottomArgs,
config::{layout::Row, IgnoreList, StringOrNum},
};
use crate::{
app::{filter::Filter, layout_manager::*, *},
canvas::{components::time_chart::LegendPosition, styling::CanvasStyling, ColourScheme},
@ -36,8 +38,8 @@ use crate::{
};
macro_rules! is_flag_enabled {
($flag_name:ident, $matches:expr, $config:expr) => {
if $matches.get_flag(stringify!($flag_name)) {
($flag_name:ident, $arg:expr, $config:expr) => {
if $arg.$flag_name {
true
} else if let Some(flags) = &$config.flags {
flags.$flag_name.unwrap_or(false)
@ -58,29 +60,34 @@ macro_rules! is_flag_enabled {
}
pub fn init_app(
matches: ArgMatches, config: Config, widget_layout: &BottomLayout, default_widget_id: u64,
default_widget_type_option: &Option<BottomWidgetType>, styling: &CanvasStyling,
) -> Result<App> {
args: BottomArgs, config: ConfigV1, styling: &CanvasStyling,
) -> Result<(App, BottomLayout)> {
use BottomWidgetType::*;
// Since everything takes a reference, but we want to take ownership here to drop matches/config later...
let matches = &matches;
let args = &args;
let config = &config;
let retention_ms =
get_retention(matches, config).context("Update `retention` in your config file.")?;
let autohide_time = is_flag_enabled!(autohide_time, matches, config);
let default_time_value = get_default_time_value(matches, config, retention_ms)
.context("Update 'default_time_value' in your config file.")?;
let (widget_layout, default_widget_id, default_widget_type_option) =
get_widget_layout(args, config)
.context("Found an issue while trying to build the widget layout.")?;
let use_basic_mode = is_flag_enabled!(basic, matches, config);
let expanded = is_flag_enabled!(expanded, matches, config);
let retention_ms = get_retention(args, config)?;
let autohide_time = is_flag_enabled!(autohide_time, args.general, config);
let default_time_value = get_default_time_value(args, config, retention_ms)?;
let use_basic_mode = is_flag_enabled!(basic, args.general, config);
let expanded = is_flag_enabled!(expanded, args.general, config);
// For processes
let is_grouped = is_flag_enabled!(group_processes, matches, config);
let is_case_sensitive = is_flag_enabled!(case_sensitive, matches, config);
let is_match_whole_word = is_flag_enabled!(whole_word, matches, config);
let is_use_regex = is_flag_enabled!(regex, matches, config);
let is_grouped = is_flag_enabled!(group_processes, args.process, config);
let is_case_sensitive = is_flag_enabled!(case_sensitive, args.process, config);
let is_match_whole_word = is_flag_enabled!(whole_word, args.process, config);
let is_use_regex = is_flag_enabled!(regex, args.process, config);
let is_default_tree = is_flag_enabled!(tree, args.process, config);
let is_default_command = is_flag_enabled!(process_command, args.process, config);
let is_advanced_kill = !(is_flag_enabled!(disable_advanced_kill, args.process, config));
let process_memory_as_value = is_flag_enabled!(process_memory_as_value, args.process, config);
let mut widget_map = HashMap::new();
let mut cpu_state_map: HashMap<u64, CpuWidgetState> = HashMap::new();
@ -102,14 +109,10 @@ pub fn init_app(
let is_custom_layout = config.row.is_some();
let mut used_widget_set = HashSet::new();
let show_memory_as_values = is_flag_enabled!(mem_as_value, matches, config);
let is_default_tree = is_flag_enabled!(tree, matches, config);
let is_default_command = is_flag_enabled!(process_command, matches, config);
let is_advanced_kill = !(is_flag_enabled!(disable_advanced_kill, matches, config));
let network_unit_type = get_network_unit_type(matches, config);
let network_scale_type = get_network_scale_type(matches, config);
let network_use_binary_prefix = is_flag_enabled!(network_use_binary_prefix, matches, config);
let network_unit_type = get_network_unit_type(args, config);
let network_scale_type = get_network_scale_type(args, config);
let network_use_binary_prefix =
is_flag_enabled!(network_use_binary_prefix, args.network, config);
let proc_columns: Option<IndexSet<ProcWidgetColumn>> = {
let columns = config.processes.as_ref().map(|cfg| cfg.columns.clone());
@ -126,32 +129,34 @@ pub fn init_app(
}
};
let network_legend_position = get_network_legend(matches, config)?;
let memory_legend_position = get_memory_legend(matches, config)?;
let network_legend_position = get_network_legend_position(args, config)?;
let memory_legend_position = get_memory_legend_position(args, config)?;
// TODO: Can probably just reuse the options struct.
let app_config_fields = AppConfigFields {
update_rate: get_update_rate(matches, config)
.context("Update 'rate' in your config file.")?,
temperature_type: get_temperature(matches, config)
update_rate: get_update_rate(args, config)?,
temperature_type: get_temperature(args, config)
.context("Update 'temperature_type' in your config file.")?,
show_average_cpu: get_show_average_cpu(matches, config),
use_dot: is_flag_enabled!(dot_marker, matches, config),
cpu_left_legend: is_flag_enabled!(cpu_left_legend, matches, config),
use_current_cpu_total: is_flag_enabled!(current_usage, matches, config),
unnormalized_cpu: is_flag_enabled!(unnormalized_cpu, matches, config),
show_average_cpu: get_show_average_cpu(args, config),
use_dot: is_flag_enabled!(dot_marker, args.general, config),
cpu_left_legend: is_flag_enabled!(cpu_left_legend, args.cpu, config),
use_current_cpu_total: is_flag_enabled!(current_usage, args.process, config),
unnormalized_cpu: is_flag_enabled!(unnormalized_cpu, args.process, config),
use_basic_mode,
default_time_value,
time_interval: get_time_interval(matches, config, retention_ms)
.context("Update 'time_delta' in your config file.")?,
hide_time: is_flag_enabled!(hide_time, matches, config),
time_interval: get_time_interval(args, config, retention_ms)?,
hide_time: is_flag_enabled!(hide_time, args.general, config),
autohide_time,
use_old_network_legend: is_flag_enabled!(use_old_network_legend, matches, config),
table_gap: u16::from(!(is_flag_enabled!(hide_table_gap, matches, config))),
disable_click: is_flag_enabled!(disable_click, matches, config),
enable_gpu: get_enable_gpu(matches, config),
enable_cache_memory: get_enable_cache_memory(matches, config),
show_table_scroll_position: is_flag_enabled!(show_table_scroll_position, matches, config),
use_old_network_legend: is_flag_enabled!(use_old_network_legend, args.network, config),
table_gap: u16::from(!(is_flag_enabled!(hide_table_gap, args.general, config))),
disable_click: is_flag_enabled!(disable_click, args.general, config),
enable_gpu: get_enable_gpu(args, config),
enable_cache_memory: get_enable_cache_memory(args, config),
show_table_scroll_position: is_flag_enabled!(
show_table_scroll_position,
args.general,
config
),
is_advanced_kill,
memory_legend_position,
network_legend_position,
@ -165,7 +170,7 @@ pub fn init_app(
is_case_sensitive,
is_match_whole_word,
is_use_regex,
show_memory_as_values,
show_memory_as_values: process_memory_as_value,
is_command: is_default_command,
};
@ -310,8 +315,8 @@ pub fn init_app(
let used_widgets = UsedWidgets {
use_cpu: used_widget_set.get(&Cpu).is_some() || used_widget_set.get(&BasicCpu).is_some(),
use_mem,
use_cache: use_mem && get_enable_cache_memory(matches, config),
use_gpu: get_enable_gpu(matches, config),
use_cache: use_mem && get_enable_cache_memory(args, config),
use_gpu: get_enable_gpu(args, config),
use_net: used_widget_set.get(&Net).is_some() || used_widget_set.get(&BasicNet).is_some(),
use_proc: used_widget_set.get(&Proc).is_some(),
use_disk: used_widget_set.get(&Disk).is_some(),
@ -348,37 +353,40 @@ pub fn init_app(
};
let is_expanded = expanded && !use_basic_mode;
Ok(App::new(
app_config_fields,
states,
widget_map,
current_widget,
used_widgets,
filters,
is_expanded,
Ok((
App::new(
app_config_fields,
states,
widget_map,
current_widget,
used_widgets,
filters,
is_expanded,
),
widget_layout,
))
}
pub fn get_widget_layout(
matches: &ArgMatches, config: &Config,
args: &BottomArgs, config: &ConfigV1,
) -> error::Result<(BottomLayout, u64, Option<BottomWidgetType>)> {
let cpu_left_legend = is_flag_enabled!(cpu_left_legend, matches, config);
let cpu_left_legend = is_flag_enabled!(cpu_left_legend, args.cpu, config);
let (default_widget_type, mut default_widget_count) =
get_default_widget_and_count(matches, config)?;
get_default_widget_and_count(args, config)?;
let mut default_widget_id = 1;
let bottom_layout = if is_flag_enabled!(basic, matches, config) {
let bottom_layout = if is_flag_enabled!(basic, args.general, config) {
default_widget_id = DEFAULT_WIDGET_ID;
BottomLayout::init_basic_default(get_use_battery(matches, config))
BottomLayout::init_basic_default(get_use_battery(args, config))
} else {
let ref_row: Vec<Row>; // Required to handle reference
let rows = match &config.row {
Some(r) => r,
None => {
// This cannot (like it really shouldn't) fail!
ref_row = toml_edit::de::from_str::<Config>(if get_use_battery(matches, config) {
ref_row = toml_edit::de::from_str::<ConfigV1>(if get_use_battery(args, config) {
DEFAULT_BATTERY_LAYOUT
} else {
DEFAULT_LAYOUT
@ -423,13 +431,17 @@ pub fn get_widget_layout(
Ok((bottom_layout, default_widget_id, default_widget_type))
}
fn get_update_rate(matches: &ArgMatches, config: &Config) -> error::Result<u64> {
let update_rate = if let Some(update_rate) = matches.get_one::<String>("rate") {
try_parse_ms(update_rate)?
fn get_update_rate(args: &BottomArgs, config: &ConfigV1) -> error::Result<u64> {
let update_rate = if let Some(update_rate) = &args.general.rate {
try_parse_ms(update_rate).map_err(|_| {
BottomError::ArgumentError("set your update rate to be valid".to_string())
})?
} else if let Some(flags) = &config.flags {
if let Some(rate) = &flags.rate {
match rate {
StringOrNum::String(s) => try_parse_ms(s)?,
StringOrNum::String(s) => try_parse_ms(s).map_err(|_| {
BottomError::ConfigError("set your update rate to be valid".to_string())
})?,
StringOrNum::Num(n) => *n,
}
} else {
@ -448,32 +460,24 @@ fn get_update_rate(matches: &ArgMatches, config: &Config) -> error::Result<u64>
Ok(update_rate)
}
fn get_temperature(matches: &ArgMatches, config: &Config) -> error::Result<TemperatureType> {
if matches.get_flag("fahrenheit") {
fn get_temperature(args: &BottomArgs, config: &ConfigV1) -> error::Result<TemperatureType> {
if args.temperature.fahrenheit {
return Ok(TemperatureType::Fahrenheit);
} else if matches.get_flag("kelvin") {
} else if args.temperature.kelvin {
return Ok(TemperatureType::Kelvin);
} else if matches.get_flag("celsius") {
} else if args.temperature.celsius {
return Ok(TemperatureType::Celsius);
} else if let Some(flags) = &config.flags {
if let Some(temp_type) = &flags.temperature_type {
// Give lowest priority to config.
return match temp_type.as_str() {
"fahrenheit" | "f" => Ok(TemperatureType::Fahrenheit),
"kelvin" | "k" => Ok(TemperatureType::Kelvin),
"celsius" | "c" => Ok(TemperatureType::Celsius),
_ => Err(BottomError::ConfigError(format!(
"\"{temp_type}\" is an invalid temperature type, use \"<kelvin|k|celsius|c|fahrenheit|f>\"."
))),
};
return TemperatureType::from_str(temp_type).map_err(BottomError::ConfigError);
}
}
Ok(TemperatureType::Celsius)
}
/// Yes, this function gets whether to show average CPU (true) or not (false)
fn get_show_average_cpu(matches: &ArgMatches, config: &Config) -> bool {
if matches.get_flag("hide_avg_cpu") {
/// Yes, this function gets whether to show average CPU (true) or not (false).
fn get_show_average_cpu(args: &BottomArgs, config: &ConfigV1) -> bool {
if args.cpu.hide_avg_cpu {
return false;
} else if let Some(flags) = &config.flags {
if let Some(avg_cpu) = flags.hide_avg_cpu {
@ -497,31 +501,34 @@ fn try_parse_ms(s: &str) -> error::Result<u64> {
}
fn get_default_time_value(
matches: &ArgMatches, config: &Config, retention_ms: u64,
args: &BottomArgs, config: &ConfigV1, retention_ms: u64,
) -> error::Result<u64> {
let default_time =
if let Some(default_time_value) = matches.get_one::<String>("default_time_value") {
try_parse_ms(default_time_value)?
} else if let Some(flags) = &config.flags {
if let Some(default_time_value) = &flags.default_time_value {
match default_time_value {
StringOrNum::String(s) => try_parse_ms(s)?,
StringOrNum::Num(n) => *n,
}
} else {
DEFAULT_TIME_MILLISECONDS
let default_time = if let Some(default_time_value) = &args.general.default_time_value {
try_parse_ms(default_time_value).map_err(|_| {
BottomError::ArgumentError("set your default time to be valid".to_string())
})?
} else if let Some(flags) = &config.flags {
if let Some(default_time_value) = &flags.default_time_value {
match default_time_value {
StringOrNum::String(s) => try_parse_ms(s).map_err(|_| {
BottomError::ConfigError("set your default time to be valid".to_string())
})?,
StringOrNum::Num(n) => *n,
}
} else {
DEFAULT_TIME_MILLISECONDS
};
}
} else {
DEFAULT_TIME_MILLISECONDS
};
if default_time < 30000 {
return Err(BottomError::ConfigError(
"set your default value to be at least 30s.".to_string(),
"set your default time to be at least 30s.".to_string(),
));
} else if default_time > retention_ms {
return Err(BottomError::ConfigError(format!(
"set your default value to be at most {}.",
"set your default time to be at most {}.",
humantime::Duration::from(Duration::from_millis(retention_ms))
)));
}
@ -530,14 +537,18 @@ fn get_default_time_value(
}
fn get_time_interval(
matches: &ArgMatches, config: &Config, retention_ms: u64,
args: &BottomArgs, config: &ConfigV1, retention_ms: u64,
) -> error::Result<u64> {
let time_interval = if let Some(time_interval) = matches.get_one::<String>("time_delta") {
try_parse_ms(time_interval)?
let time_interval = if let Some(time_interval) = &args.general.time_delta {
try_parse_ms(time_interval).map_err(|_| {
BottomError::ArgumentError("set your time delta to be valid".to_string())
})?
} else if let Some(flags) = &config.flags {
if let Some(time_interval) = &flags.time_delta {
match time_interval {
StringOrNum::String(s) => try_parse_ms(s)?,
StringOrNum::String(s) => try_parse_ms(s).map_err(|_| {
BottomError::ArgumentError("set your time delta to be valid".to_string())
})?,
StringOrNum::Num(n) => *n,
}
} else {
@ -562,9 +573,9 @@ fn get_time_interval(
}
fn get_default_widget_and_count(
matches: &ArgMatches, config: &Config,
args: &BottomArgs, config: &ConfigV1,
) -> error::Result<(Option<BottomWidgetType>, u64)> {
let widget_type = if let Some(widget_type) = matches.get_one::<String>("default_widget_type") {
let widget_type = if let Some(widget_type) = &args.general.default_widget_type {
let parsed_widget = widget_type.parse::<BottomWidgetType>()?;
if let BottomWidgetType::Empty = parsed_widget {
None
@ -586,21 +597,20 @@ fn get_default_widget_and_count(
None
};
let widget_count = if let Some(widget_count) = matches.get_one::<String>("default_widget_count")
{
Some(widget_count.parse::<u128>()?)
} else if let Some(flags) = &config.flags {
flags
.default_widget_count
.map(|widget_count| widget_count.into())
let widget_count: Option<u128> = if let Some(widget_count) = args.general.default_widget_count {
Some(widget_count.into())
} else {
None
config.flags.as_ref().and_then(|flags| {
flags
.default_widget_count
.map(|widget_count| widget_count.into())
})
};
match (widget_type, widget_count) {
(Some(widget_type), Some(widget_count)) => {
let widget_count = widget_count.try_into().map_err(|_| BottomError::ConfigError(
"set your widget count to be at most unsigned INT_MAX.".to_string()
"set your widget count to be at most 18446744073709551615.".to_string()
))?;
Ok((Some(widget_type), widget_count))
}
@ -613,9 +623,10 @@ fn get_default_widget_and_count(
}
#[allow(unused_variables)]
fn get_use_battery(matches: &ArgMatches, config: &Config) -> bool {
fn get_use_battery(args: &BottomArgs, config: &ConfigV1) -> bool {
#[cfg(feature = "battery")]
{
// TODO: Move this so it's dynamic in the app itself and automatically hide if there are no batteries?
if let Ok(battery_manager) = Manager::new() {
if let Ok(batteries) = battery_manager.batteries() {
if batteries.count() == 0 {
@ -624,7 +635,7 @@ fn get_use_battery(matches: &ArgMatches, config: &Config) -> bool {
}
}
if matches.get_flag("battery") {
if args.battery.battery {
return true;
} else if let Some(flags) = &config.flags {
if let Some(battery) = flags.battery {
@ -637,10 +648,10 @@ fn get_use_battery(matches: &ArgMatches, config: &Config) -> bool {
}
#[allow(unused_variables)]
fn get_enable_gpu(matches: &ArgMatches, config: &Config) -> bool {
fn get_enable_gpu(args: &BottomArgs, config: &ConfigV1) -> bool {
#[cfg(feature = "gpu")]
{
if matches.get_flag("enable_gpu") {
if args.gpu.enable_gpu {
return true;
} else if let Some(flags) = &config.flags {
if let Some(enable_gpu) = flags.enable_gpu {
@ -653,10 +664,10 @@ fn get_enable_gpu(matches: &ArgMatches, config: &Config) -> bool {
}
#[allow(unused_variables)]
fn get_enable_cache_memory(matches: &ArgMatches, config: &Config) -> bool {
fn get_enable_cache_memory(args: &BottomArgs, config: &ConfigV1) -> bool {
#[cfg(not(target_os = "windows"))]
{
if matches.get_flag("enable_cache_memory") {
if args.memory.enable_cache_memory {
return true;
} else if let Some(flags) = &config.flags {
if let Some(enable_cache_memory) = flags.enable_cache_memory {
@ -705,8 +716,8 @@ fn get_ignore_list(ignore_list: &Option<IgnoreList>) -> error::Result<Option<Fil
}
}
pub fn get_color_scheme(matches: &ArgMatches, config: &Config) -> error::Result<ColourScheme> {
if let Some(color) = matches.get_one::<String>("color") {
pub fn get_color_scheme(args: &BottomArgs, config: &ConfigV1) -> error::Result<ColourScheme> {
if let Some(color) = &args.style.color {
// Highest priority is always command line flags...
return ColourScheme::from_str(color);
} else if let Some(colors) = &config.colors {
@ -730,8 +741,8 @@ pub fn get_color_scheme(matches: &ArgMatches, config: &Config) -> error::Result<
Ok(ColourScheme::Default)
}
fn get_network_unit_type(matches: &ArgMatches, config: &Config) -> DataUnit {
if matches.get_flag("network_use_bytes") {
fn get_network_unit_type(args: &BottomArgs, config: &ConfigV1) -> DataUnit {
if args.network.network_use_bytes {
return DataUnit::Byte;
} else if let Some(flags) = &config.flags {
if let Some(network_use_bytes) = flags.network_use_bytes {
@ -744,8 +755,8 @@ fn get_network_unit_type(matches: &ArgMatches, config: &Config) -> DataUnit {
DataUnit::Bit
}
fn get_network_scale_type(matches: &ArgMatches, config: &Config) -> AxisScaling {
if matches.get_flag("network_use_log") {
fn get_network_scale_type(args: &BottomArgs, config: &ConfigV1) -> AxisScaling {
if args.network.network_use_log {
return AxisScaling::Log;
} else if let Some(flags) = &config.flags {
if let Some(network_use_log) = flags.network_use_log {
@ -758,15 +769,18 @@ fn get_network_scale_type(matches: &ArgMatches, config: &Config) -> AxisScaling
AxisScaling::Linear
}
fn get_retention(matches: &ArgMatches, config: &Config) -> error::Result<u64> {
fn get_retention(args: &BottomArgs, config: &ConfigV1) -> error::Result<u64> {
const DEFAULT_RETENTION_MS: u64 = 600 * 1000; // Keep 10 minutes of data.
if let Some(retention) = matches.get_one::<String>("retention") {
if let Some(retention) = &args.general.retention {
try_parse_ms(retention)
.map_err(|_| BottomError::ArgumentError("`retention` is an invalid value".to_string()))
} else if let Some(flags) = &config.flags {
if let Some(retention) = &flags.retention {
Ok(match retention {
StringOrNum::String(s) => try_parse_ms(s)?,
StringOrNum::String(s) => try_parse_ms(s).map_err(|_| {
BottomError::ConfigError("`retention` is an invalid value".to_string())
})?,
StringOrNum::Num(n) => *n,
})
} else {
@ -777,19 +791,21 @@ fn get_retention(matches: &ArgMatches, config: &Config) -> error::Result<u64> {
}
}
fn get_network_legend(
matches: &ArgMatches, config: &Config,
fn get_network_legend_position(
args: &BottomArgs, config: &ConfigV1,
) -> error::Result<Option<LegendPosition>> {
let error =
|_| BottomError::ConfigError("network_legend is set to an invalid value".to_string());
if let Some(s) = matches.get_one::<String>("network_legend") {
if let Some(s) = &args.network.network_legend {
match s.to_ascii_lowercase().trim() {
"none" => Ok(None),
position => Ok(Some(position.parse::<LegendPosition>().map_err(error)?)),
position => Ok(Some(position.parse::<LegendPosition>().map_err(|_| {
BottomError::ArgumentError("`network_legend` is an invalid value".to_string())
})?)),
}
} else if let Some(flags) = &config.flags {
if let Some(legend) = &flags.network_legend {
Ok(Some(legend.parse::<LegendPosition>().map_err(error)?))
Ok(Some(legend.parse::<LegendPosition>().map_err(|_| {
BottomError::ConfigError("`network_legend` is an invalid value".to_string())
})?))
} else {
Ok(Some(LegendPosition::default()))
}
@ -798,19 +814,21 @@ fn get_network_legend(
}
}
fn get_memory_legend(
matches: &ArgMatches, config: &Config,
fn get_memory_legend_position(
args: &BottomArgs, config: &ConfigV1,
) -> error::Result<Option<LegendPosition>> {
let error =
|_| BottomError::ConfigError("memory_legend is set to an invalid value".to_string());
if let Some(s) = matches.get_one::<String>("memory_legend") {
if let Some(s) = &args.memory.memory_legend {
match s.to_ascii_lowercase().trim() {
"none" => Ok(None),
position => Ok(Some(position.parse::<LegendPosition>().map_err(error)?)),
position => Ok(Some(position.parse::<LegendPosition>().map_err(|_| {
BottomError::ArgumentError("`memory_legend` is an invalid value".to_string())
})?)),
}
} else if let Some(flags) = &config.flags {
if let Some(legend) = &flags.memory_legend {
Ok(Some(legend.parse::<LegendPosition>().map_err(error)?))
Ok(Some(legend.parse::<LegendPosition>().map_err(|_| {
BottomError::ConfigError("`memory_legend` is an invalid value".to_string())
})?))
} else {
Ok(Some(LegendPosition::default()))
}
@ -821,11 +839,12 @@ fn get_memory_legend(
#[cfg(test)]
mod test {
use clap::ArgMatches;
use clap::Parser;
use super::{get_color_scheme, get_time_interval, get_widget_layout, Config};
use super::{get_color_scheme, get_time_interval, ConfigV1};
use crate::{
app::App,
args::BottomArgs,
canvas::styling::CanvasStyling,
options::{
config::ConfigFlags, get_default_time_value, get_retention, get_update_rate,
@ -854,26 +873,24 @@ mod test {
#[test]
fn matches_human_times() {
let config = Config::default();
let app = crate::args::build_app();
let config = ConfigV1::default();
{
let app = app.clone();
let delta_args = vec!["btm", "--time_delta", "2 min"];
let matches = app.get_matches_from(delta_args);
let args = BottomArgs::parse_from(delta_args);
assert_eq!(
get_time_interval(&matches, &config, 60 * 60 * 1000),
get_time_interval(&args, &config, 60 * 60 * 1000),
Ok(2 * 60 * 1000)
);
}
{
let default_time_args = vec!["btm", "--default_time_value", "300s"];
let matches = app.get_matches_from(default_time_args);
let args = BottomArgs::parse_from(default_time_args);
assert_eq!(
get_default_time_value(&matches, &config, 60 * 60 * 1000),
get_default_time_value(&args, &config, 60 * 60 * 1000),
Ok(5 * 60 * 1000)
);
}
@ -881,26 +898,24 @@ mod test {
#[test]
fn matches_number_times() {
let config = Config::default();
let app = crate::args::build_app();
let config = ConfigV1::default();
{
let app = app.clone();
let delta_args = vec!["btm", "--time_delta", "120000"];
let matches = app.get_matches_from(delta_args);
let args = BottomArgs::parse_from(delta_args);
assert_eq!(
get_time_interval(&matches, &config, 60 * 60 * 1000),
get_time_interval(&args, &config, 60 * 60 * 1000),
Ok(2 * 60 * 1000)
);
}
{
let default_time_args = vec!["btm", "--default_time_value", "300000"];
let matches = app.get_matches_from(default_time_args);
let args = BottomArgs::parse_from(default_time_args);
assert_eq!(
get_default_time_value(&matches, &config, 60 * 60 * 1000),
get_default_time_value(&args, &config, 60 * 60 * 1000),
Ok(5 * 60 * 1000)
);
}
@ -908,10 +923,9 @@ mod test {
#[test]
fn config_human_times() {
let app = crate::args::build_app();
let matches = app.get_matches_from(["btm"]);
let args = BottomArgs::parse_from(["btm"]);
let mut config = Config::default();
let mut config = ConfigV1::default();
let flags = ConfigFlags {
time_delta: Some("2 min".to_string().into()),
default_time_value: Some("300s".to_string().into()),
@ -923,26 +937,25 @@ mod test {
config.flags = Some(flags);
assert_eq!(
get_time_interval(&matches, &config, 60 * 60 * 1000),
get_time_interval(&args, &config, 60 * 60 * 1000),
Ok(2 * 60 * 1000)
);
assert_eq!(
get_default_time_value(&matches, &config, 60 * 60 * 1000),
get_default_time_value(&args, &config, 60 * 60 * 1000),
Ok(5 * 60 * 1000)
);
assert_eq!(get_update_rate(&matches, &config), Ok(1000));
assert_eq!(get_update_rate(&args, &config), Ok(1000));
assert_eq!(get_retention(&matches, &config), Ok(600000));
assert_eq!(get_retention(&args, &config), Ok(600000));
}
#[test]
fn config_number_times_as_string() {
let app = crate::args::build_app();
let matches = app.get_matches_from(["btm"]);
let args = BottomArgs::parse_from(["btm"]);
let mut config = Config::default();
let mut config = ConfigV1::default();
let flags = ConfigFlags {
time_delta: Some("120000".to_string().into()),
default_time_value: Some("300000".to_string().into()),
@ -954,26 +967,25 @@ mod test {
config.flags = Some(flags);
assert_eq!(
get_time_interval(&matches, &config, 60 * 60 * 1000),
get_time_interval(&args, &config, 60 * 60 * 1000),
Ok(2 * 60 * 1000)
);
assert_eq!(
get_default_time_value(&matches, &config, 60 * 60 * 1000),
get_default_time_value(&args, &config, 60 * 60 * 1000),
Ok(5 * 60 * 1000)
);
assert_eq!(get_update_rate(&matches, &config), Ok(1000));
assert_eq!(get_update_rate(&args, &config), Ok(1000));
assert_eq!(get_retention(&matches, &config), Ok(600000));
assert_eq!(get_retention(&args, &config), Ok(600000));
}
#[test]
fn config_number_times_as_num() {
let app = crate::args::build_app();
let matches = app.get_matches_from(["btm"]);
let args = BottomArgs::parse_from(["btm"]);
let mut config = Config::default();
let mut config = ConfigV1::default();
let flags = ConfigFlags {
time_delta: Some(120000.into()),
default_time_value: Some(300000.into()),
@ -985,41 +997,35 @@ mod test {
config.flags = Some(flags);
assert_eq!(
get_time_interval(&matches, &config, 60 * 60 * 1000),
get_time_interval(&args, &config, 60 * 60 * 1000),
Ok(2 * 60 * 1000)
);
assert_eq!(
get_default_time_value(&matches, &config, 60 * 60 * 1000),
get_default_time_value(&args, &config, 60 * 60 * 1000),
Ok(5 * 60 * 1000)
);
assert_eq!(get_update_rate(&matches, &config), Ok(1000));
assert_eq!(get_update_rate(&args, &config), Ok(1000));
assert_eq!(get_retention(&matches, &config), Ok(600000));
assert_eq!(get_retention(&args, &config), Ok(600000));
}
fn create_app(config: Config, matches: ArgMatches) -> App {
let (layout, id, ty) = get_widget_layout(&matches, &config).unwrap();
fn create_app(args: BottomArgs) -> App {
let config = ConfigV1::default();
let styling =
CanvasStyling::new(get_color_scheme(&matches, &config).unwrap(), &config).unwrap();
CanvasStyling::new(get_color_scheme(&args, &config).unwrap(), &config).unwrap();
super::init_app(matches, config, &layout, id, &ty, &styling).unwrap()
super::init_app(args, config, &styling).unwrap().0
}
// TODO: There's probably a better way to create clap options AND unify together to avoid the possibility of
// typos/mixing up. Use proc macros to unify on one struct?
#[test]
fn verify_cli_options_build() {
let app = crate::args::build_app();
let app = crate::args::build_cmd();
let default_app = {
let app = app.clone();
let config = Config::default();
let matches = app.get_matches_from([""]);
create_app(config, matches)
};
let default_app = create_app(BottomArgs::parse_from(["btm"]));
// Skip battery since it's tricky to test depending on the platform/features we're testing with.
let skip = ["help", "version", "celsius", "battery"];
@ -1036,11 +1042,8 @@ mod test {
let arg = format!("--{arg_name}");
let arguments = vec!["btm", &arg];
let app = app.clone();
let config = Config::default();
let matches = app.get_matches_from(arguments);
let testing_app = create_app(config, matches);
let args = BottomArgs::parse_from(arguments);
let testing_app = create_app(args);
if (default_app.app_config_fields == testing_app.app_config_fields)
&& default_app.is_expanded == testing_app.is_expanded

File diff suppressed because it is too large Load diff

View file

@ -10,7 +10,7 @@ use self::{cpu::CpuConfig, layout::Row, process_columns::ProcessConfig};
use super::ConfigColours;
#[derive(Clone, Debug, Default, Deserialize)]
pub struct Config {
pub struct ConfigV1 {
pub(crate) flags: Option<ConfigFlags>,
pub(crate) colors: Option<ConfigColours>,
pub(crate) row: Option<Vec<Row>>,
@ -71,7 +71,7 @@ pub(crate) struct ConfigFlags {
pub(crate) memory_legend: Option<String>,
/// For built-in colour palettes.
pub(crate) color: Option<String>,
pub(crate) mem_as_value: Option<bool>,
pub(crate) process_memory_as_value: Option<bool>,
pub(crate) tree: Option<bool>,
pub(crate) show_table_scroll_position: Option<bool>,
pub(crate) process_command: Option<bool>,

View file

@ -239,7 +239,7 @@ mod test {
use super::*;
use crate::{
constants::{DEFAULT_LAYOUT, DEFAULT_WIDGET_ID},
options::Config,
options::ConfigV1,
utils::error,
};
@ -293,7 +293,7 @@ mod test {
#[test]
/// Tests the default setup.
fn test_default_movement() {
let rows = from_str::<Config>(DEFAULT_LAYOUT).unwrap().row.unwrap();
let rows = from_str::<ConfigV1>(DEFAULT_LAYOUT).unwrap().row.unwrap();
let ret_bottom_layout = test_create_layout(&rows, DEFAULT_WIDGET_ID, None, 1, false);
// Simple tests for the top CPU widget
@ -367,7 +367,7 @@ mod test {
fn test_default_battery_movement() {
use crate::constants::DEFAULT_BATTERY_LAYOUT;
let rows = from_str::<Config>(DEFAULT_BATTERY_LAYOUT)
let rows = from_str::<ConfigV1>(DEFAULT_BATTERY_LAYOUT)
.unwrap()
.row
.unwrap();
@ -413,7 +413,7 @@ mod test {
#[test]
/// Tests using cpu_left_legend.
fn test_cpu_left_legend() {
let rows = from_str::<Config>(DEFAULT_LAYOUT).unwrap().row.unwrap();
let rows = from_str::<ConfigV1>(DEFAULT_LAYOUT).unwrap().row.unwrap();
let ret_bottom_layout = test_create_layout(&rows, DEFAULT_WIDGET_ID, None, 1, true);
// Legend
@ -473,7 +473,7 @@ mod test {
type="proc"
"#;
let rows = from_str::<Config>(proc_layout).unwrap().row.unwrap();
let rows = from_str::<ConfigV1>(proc_layout).unwrap().row.unwrap();
let mut iter_id = 0; // A lazy way of forcing unique IDs *shrugs*
let mut total_height_ratio = 0;
let mut default_widget_count = 1;
@ -506,7 +506,7 @@ mod test {
#[test]
/// Tests default widget by setting type and count.
fn test_default_widget_by_option() {
let rows = from_str::<Config>(PROC_LAYOUT).unwrap().row.unwrap();
let rows = from_str::<ConfigV1>(PROC_LAYOUT).unwrap().row.unwrap();
let mut iter_id = 0; // A lazy way of forcing unique IDs *shrugs*
let mut total_height_ratio = 0;
let mut default_widget_count = 3;
@ -538,7 +538,7 @@ mod test {
#[test]
fn test_proc_custom_layout() {
let rows = from_str::<Config>(PROC_LAYOUT).unwrap().row.unwrap();
let rows = from_str::<ConfigV1>(PROC_LAYOUT).unwrap().row.unwrap();
let ret_bottom_layout = test_create_layout(&rows, DEFAULT_WIDGET_ID, None, 1, false);
// First proc widget

View file

@ -17,21 +17,22 @@ pub enum BottomError {
/// An error to represent generic errors.
#[error("Error, {0}")]
GenericError(String),
#[cfg(feature = "fern")]
/// An error to represent errors with fern.
#[error("Fern error, {0}")]
FernError(String),
/// An error to represent invalid command-line arguments.
#[error("Invalid argument, {0}")]
ArgumentError(String),
/// An error to represent errors with the config.
#[error("Configuration file error, {0}")]
ConfigError(String),
/// An error to represent errors with converting between data types.
#[error("Conversion error, {0}")]
ConversionError(String),
/// An error to represent errors with querying.
/// An error to represent errors with a query.
#[error("Query error, {0}")]
QueryError(Cow<'static, str>),
/// An error that just signifies something minor went wrong; no message.
#[error("Minor error.")]
MinorError,
#[error("Error casting integers {0}")]
TryFromIntError(#[from] std::num::TryFromIntError),
}

View file

@ -255,7 +255,7 @@ impl ProcWidgetState {
let is_count = matches!(mode, ProcWidgetMode::Grouped);
let is_command = table_config.is_command;
let mem_vals = table_config.show_memory_as_values;
let mem_as_values = table_config.show_memory_as_values;
match config_columns {
Some(columns) if !columns.is_empty() => columns
@ -278,7 +278,7 @@ impl ProcWidgetState {
}
ProcWidgetColumn::Cpu => CpuPercent,
ProcWidgetColumn::Mem => {
if mem_vals {
if mem_as_values {
MemoryVal
} else {
MemoryPercent
@ -293,7 +293,7 @@ impl ProcWidgetState {
ProcWidgetColumn::Time => Time,
#[cfg(feature = "gpu")]
ProcWidgetColumn::GpuMem => {
if mem_vals {
if mem_as_values {
GpuMem
} else {
GpuMemPercent
@ -311,7 +311,11 @@ impl ProcWidgetState {
if is_count { Count } else { Pid },
if is_command { Command } else { Name },
CpuPercent,
if mem_vals { MemoryVal } else { MemoryPercent },
if mem_as_values {
MemoryVal
} else {
MemoryPercent
},
ReadPerSecond,
WritePerSecond,
TotalRead,

View file

@ -24,7 +24,9 @@ fn test_large_default_time() {
.arg("18446744073709551616")
.assert()
.failure()
.stderr(predicate::str::contains("could not parse"));
.stderr(predicate::str::contains(
"set your default time to be valid",
));
}
#[test]
@ -35,7 +37,7 @@ fn test_small_default_time() {
.assert()
.failure()
.stderr(predicate::str::contains(
"set your default value to be at least",
"set your default time to be at least",
));
}
@ -46,7 +48,7 @@ fn test_large_delta_time() {
.arg("18446744073709551616")
.assert()
.failure()
.stderr(predicate::str::contains("could not parse"));
.stderr(predicate::str::contains("set your time delta to be valid"));
}
#[test]
@ -68,7 +70,7 @@ fn test_large_rate() {
.arg("18446744073709551616")
.assert()
.failure()
.stderr(predicate::str::contains("could not parse"));
.stderr(predicate::str::contains("set your update rate"));
}
#[test]
@ -89,7 +91,7 @@ fn test_invalid_rate() {
.arg("100-1000")
.assert()
.failure()
.stderr(predicate::str::contains("could not parse"));
.stderr(predicate::str::contains("set your update rate"));
}
#[test]
@ -121,9 +123,7 @@ fn test_invalid_default_widget_2() {
.arg("18446744073709551616")
.assert()
.failure()
.stderr(predicate::str::contains(
"set your widget count to be at most unsigned INT_MAX",
));
.stderr(predicate::str::contains("number too large"));
}
#[test]