mirror of
https://github.com/ClementTsang/bottom.git
synced 2024-11-09 04:44:55 +03:00
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:
parent
71f6136a1e
commit
ee2e1fee1c
@ -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
23
Cargo.lock
generated
@ -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",
|
||||
|
@ -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"
|
||||
|
6
build.rs
6
build.rs
@ -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)?;
|
||||
|
@ -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
|
||||
|
||||
|
@ -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. |
|
||||
|
@ -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.
|
||||
|
@ -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"
|
||||
|
@ -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)?;
|
||||
|
@ -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();
|
||||
|
@ -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.
|
||||
|
@ -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 {
|
||||
|
26
src/lib.rs
26
src/lib.rs
@ -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())
|
||||
}
|
||||
}
|
||||
|
||||
|
397
src/options.rs
397
src/options.rs
@ -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
@ -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>,
|
||||
|
@ -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
|
||||
|
@ -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),
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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]
|
||||
|
Loading…
Reference in New Issue
Block a user