mirror of
https://github.com/ClementTsang/bottom.git
synced 2024-11-14 04:42:03 +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.
|
- [#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:
|
- [#1441](https://github.com/ClementTsang/bottom/pull/1441): The following arguments have changed names:
|
||||||
- `--left_legend/-l` is now `--cpu_left_legend`.
|
- `--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`.
|
- `expanded_on_startup` is now `expanded`.
|
||||||
- `left_legend` is now `cpu_left_legend`.
|
- `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
|
### 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"
|
checksum = "90bc066a67923782aa8515dbaea16946c5bcc5addbd668bb80af688e53e548a0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"clap_builder",
|
"clap_builder",
|
||||||
|
"clap_derive",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -302,6 +303,18 @@ dependencies = [
|
|||||||
"clap_complete",
|
"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]]
|
[[package]]
|
||||||
name = "clap_lex"
|
name = "clap_lex"
|
||||||
version = "0.7.0"
|
version = "0.7.0"
|
||||||
@ -514,7 +527,7 @@ version = "0.6.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5ffccbb6966c05b32ef8fbac435df276c4ae4d3dc55a8cd0eb9745e6c12f546a"
|
checksum = "5ffccbb6966c05b32ef8fbac435df276c4ae4d3dc55a8cd0eb9745e6c12f546a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"heck",
|
"heck 0.4.1",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn",
|
"syn",
|
||||||
@ -604,6 +617,12 @@ version = "0.4.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
|
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "heck"
|
||||||
|
version = "0.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "humantime"
|
name = "humantime"
|
||||||
version = "2.1.0"
|
version = "2.1.0"
|
||||||
@ -1333,7 +1352,7 @@ version = "0.26.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c6cf59daf282c0a494ba14fd21610a0325f9f90ec9d1231dea26bcb1d696c946"
|
checksum = "c6cf59daf282c0a494ba14fd21610a0325f9f90ec9d1231dea26bcb1d696c946"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"heck",
|
"heck 0.4.1",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"rustversion",
|
"rustversion",
|
||||||
|
@ -19,7 +19,9 @@ exclude = [
|
|||||||
"desktop/",
|
"desktop/",
|
||||||
"docs/",
|
"docs/",
|
||||||
"sample_configs/",
|
"sample_configs/",
|
||||||
|
"schema",
|
||||||
"scripts/",
|
"scripts/",
|
||||||
|
"wix/",
|
||||||
".all-contributorsrc",
|
".all-contributorsrc",
|
||||||
".cirrus.yml",
|
".cirrus.yml",
|
||||||
".gitignore",
|
".gitignore",
|
||||||
@ -29,6 +31,9 @@ exclude = [
|
|||||||
"codecov.yml",
|
"codecov.yml",
|
||||||
"CONTRIBUTING.md",
|
"CONTRIBUTING.md",
|
||||||
"Cross.toml",
|
"Cross.toml",
|
||||||
|
"debug.log",
|
||||||
|
"flamegraph.svg",
|
||||||
|
"perf.data",
|
||||||
"rustfmt.toml",
|
"rustfmt.toml",
|
||||||
]
|
]
|
||||||
rust-version = "1.74.0" # The oldest version I've tested that should still build - note this is not an official MSRV!
|
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"
|
anyhow = "1.0.86"
|
||||||
backtrace = "0.3.71"
|
backtrace = "0.3.71"
|
||||||
cfg-if = "1.0.0"
|
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"
|
concat-string = "1.0.1"
|
||||||
crossterm = "0.27.0"
|
crossterm = "0.27.0"
|
||||||
ctrlc = { version = "3.4.4", features = ["termination"] }
|
ctrlc = { version = "3.4.4", features = ["termination"] }
|
||||||
@ -135,7 +140,7 @@ predicates = "3.1.0"
|
|||||||
portable-pty = "0.8.1"
|
portable-pty = "0.8.1"
|
||||||
|
|
||||||
[build-dependencies]
|
[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 = "4.5.2"
|
||||||
clap_complete_nushell = "4.5.1"
|
clap_complete_nushell = "4.5.1"
|
||||||
clap_complete_fig = "4.5.0"
|
clap_complete_fig = "4.5.0"
|
||||||
|
6
build.rs
6
build.rs
@ -7,12 +7,12 @@ use std::{
|
|||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
};
|
};
|
||||||
|
|
||||||
use clap::Command;
|
use clap::{Command, CommandFactory};
|
||||||
use clap_complete::{generate_to, shells::Shell, Generator};
|
use clap_complete::{generate_to, shells::Shell, Generator};
|
||||||
use clap_complete_fig::Fig;
|
use clap_complete_fig::Fig;
|
||||||
use clap_complete_nushell::Nushell;
|
use clap_complete_nushell::Nushell;
|
||||||
|
|
||||||
use crate::args::build_app;
|
use crate::args::BottomArgs;
|
||||||
|
|
||||||
fn create_dir(dir: &Path) -> io::Result<()> {
|
fn create_dir(dir: &Path) -> io::Result<()> {
|
||||||
let res = fs::create_dir_all(dir);
|
let res = fs::create_dir_all(dir);
|
||||||
@ -48,7 +48,7 @@ fn btm_generate() -> io::Result<()> {
|
|||||||
create_dir(&manpage_out_dir)?;
|
create_dir(&manpage_out_dir)?;
|
||||||
|
|
||||||
// Generate completions
|
// Generate completions
|
||||||
let mut app = build_app();
|
let mut app = BottomArgs::command();
|
||||||
generate_completions(Shell::Bash, &mut app, &completion_out_dir)?;
|
generate_completions(Shell::Bash, &mut app, &completion_out_dir)?;
|
||||||
generate_completions(Shell::Zsh, &mut app, &completion_out_dir)?;
|
generate_completions(Shell::Zsh, &mut app, &completion_out_dir)?;
|
||||||
generate_completions(Shell::Fish, &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
|
## Process Options
|
||||||
|
|
||||||
| Option | Behaviour |
|
| Option | Behaviour |
|
||||||
| ------------------------- | -------------------------------------------------------------------------------------- |
|
| --------------------------- | -------------------------------------------------------------------------------------- |
|
||||||
| `-S, --case_sensitive` | Enables case sensitivity by default. |
|
| `-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. |
|
| `-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. |
|
| `--disable_advanced_kill` | Hides additional stopping options Unix-like systems. |
|
||||||
| `-g, --group_processes` | Groups processes with the same name by default. |
|
| `-g, --group_processes` | Groups processes with the same name by default. |
|
||||||
| `--mem_as_value` | Defaults to showing process memory usage by value. |
|
| `--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. |
|
| `--process_command` | Shows the full command name instead of the process name by default. |
|
||||||
| `-R, --regex` | Enables regex by default while searching. |
|
| `-R, --regex` | Enables regex by default while searching. |
|
||||||
| `-T, --tree` | Makes the process widget use tree mode by default. |
|
| `-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. |
|
| `-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. |
|
| `-W, --whole_word` | Enables whole-word matching by default while searching. |
|
||||||
|
|
||||||
## Temperature Options
|
## Temperature Options
|
||||||
|
|
||||||
|
@ -18,7 +18,7 @@ each time:
|
|||||||
| ---------------------------- | ------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------- |
|
| ---------------------------- | ------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------- |
|
||||||
| `hide_avg_cpu` | Boolean | Hides the average CPU usage. |
|
| `hide_avg_cpu` | Boolean | Hides the average CPU usage. |
|
||||||
| `dot_marker` | Boolean | Uses a dot marker for graphs. |
|
| `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%. |
|
| `current_usage` | Boolean | Sets process CPU% to be based on current CPU%. |
|
||||||
| `group_processes` | Boolean | Groups processes with the same name by default. |
|
| `group_processes` | Boolean | Groups processes with the same name by default. |
|
||||||
| `case_sensitive` | Boolean | Enables case sensitivity by default. |
|
| `case_sensitive` | Boolean | Enables case sensitivity by default. |
|
||||||
@ -37,7 +37,7 @@ each time:
|
|||||||
| `disable_click` | Boolean | Disables mouse clicks. |
|
| `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. |
|
| `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). |
|
| `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. |
|
| `tree` | Boolean | Defaults to showing the process widget in tree mode. |
|
||||||
| `show_table_scroll_position` | Boolean | Shows the scroll position tracker in table widgets. |
|
| `show_table_scroll_position` | Boolean | Shows the scroll position tracker in table widgets. |
|
||||||
| `process_command` | Boolean | Show processes as their commands by default. |
|
| `process_command` | Boolean | Show processes as their commands by default. |
|
||||||
@ -48,6 +48,6 @@ each time:
|
|||||||
| `enable_gpu` | Boolean | Shows the GPU widgets. |
|
| `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. |
|
| `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. |
|
| `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. |
|
| `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. |
|
| `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"
|
# Built-in themes. Valid values are "default", "default-light", "gruvbox", "gruvbox-light", "nord", "nord-light"
|
||||||
#color = "default"
|
#color = "default"
|
||||||
# Show memory values in the processes widget as values by 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.
|
# Show tree mode by default in the processes widget.
|
||||||
#tree = false
|
#tree = false
|
||||||
# Shows an indicator in table widgets tracking where in the list you are.
|
# Shows an indicator in table widgets tracking where in the list you are.
|
||||||
|
@ -184,7 +184,7 @@
|
|||||||
"description": "Built-in themes",
|
"description": "Built-in themes",
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
"mem_as_value": {
|
"process_memory_as_value": {
|
||||||
"default": false,
|
"default": false,
|
||||||
"description": "Show memory values in the processes widget as values by default",
|
"description": "Show memory values in the processes widget as values by default",
|
||||||
"type": "boolean"
|
"type": "boolean"
|
||||||
|
@ -17,11 +17,10 @@ use bottom::{
|
|||||||
args,
|
args,
|
||||||
canvas::{self, styling::CanvasStyling},
|
canvas::{self, styling::CanvasStyling},
|
||||||
check_if_terminal, cleanup_terminal, create_collection_thread, create_input_thread,
|
check_if_terminal, cleanup_terminal, create_collection_thread, create_input_thread,
|
||||||
create_or_get_config,
|
|
||||||
data_conversion::*,
|
data_conversion::*,
|
||||||
handle_key_event_or_break, handle_mouse_event,
|
get_or_create_config, handle_key_event_or_break, handle_mouse_event,
|
||||||
options::{get_color_scheme, get_widget_layout, init_app},
|
options::{get_color_scheme, init_app},
|
||||||
panic_hook, read_config, try_drawing, update_data, BottomEvent,
|
panic_hook, try_drawing, update_data, BottomEvent,
|
||||||
};
|
};
|
||||||
use crossterm::{
|
use crossterm::{
|
||||||
event::{EnableBracketedPaste, EnableMouseCapture},
|
event::{EnableBracketedPaste, EnableMouseCapture},
|
||||||
@ -37,7 +36,7 @@ use tui::{backend::CrosstermBackend, Terminal};
|
|||||||
fn main() -> Result<()> {
|
fn main() -> Result<()> {
|
||||||
// let _profiler = dhat::Profiler::new_heap();
|
// let _profiler = dhat::Profiler::new_heap();
|
||||||
|
|
||||||
let matches = args::get_matches();
|
let args = args::get_args();
|
||||||
|
|
||||||
#[cfg(feature = "logging")]
|
#[cfg(feature = "logging")]
|
||||||
{
|
{
|
||||||
@ -50,34 +49,17 @@ fn main() -> Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Read from config file.
|
// Read from config file.
|
||||||
let config = {
|
let config = get_or_create_config(args.general.config_location.as_deref())
|
||||||
let config_path = read_config(matches.get_one::<String>("config_location"))
|
.context("Unable to parse or create the config file.")?;
|
||||||
.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.")?;
|
|
||||||
|
|
||||||
// FIXME: Should move this into build app or config
|
// FIXME: Should move this into build app or config
|
||||||
let styling = {
|
let styling = {
|
||||||
let colour_scheme = get_color_scheme(&matches, &config)?;
|
let colour_scheme = get_color_scheme(&args, &config)?;
|
||||||
CanvasStyling::new(colour_scheme, &config)?
|
CanvasStyling::new(colour_scheme, &config)?
|
||||||
};
|
};
|
||||||
|
|
||||||
// Create an "app" struct, which will control most of the program and store settings/state
|
// Create an "app" struct, which will control most of the program and store settings/state
|
||||||
let mut app = init_app(
|
let (mut app, widget_layout) = init_app(args, config, &styling)?;
|
||||||
matches,
|
|
||||||
config,
|
|
||||||
&widget_layout,
|
|
||||||
default_widget_id,
|
|
||||||
&default_widget_type_option,
|
|
||||||
&styling,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
// Create painter and set colours.
|
// Create painter and set colours.
|
||||||
let mut painter = canvas::Painter::init(widget_layout, styling)?;
|
let mut painter = canvas::Painter::init(widget_layout, styling)?;
|
||||||
|
@ -5,7 +5,7 @@ use colour_utils::*;
|
|||||||
use tui::style::{Color, Style};
|
use tui::style::{Color, Style};
|
||||||
|
|
||||||
use super::ColourScheme;
|
use super::ColourScheme;
|
||||||
pub use crate::options::Config;
|
pub use crate::options::ConfigV1;
|
||||||
use crate::{constants::*, options::colours::ConfigColours, utils::error};
|
use crate::{constants::*, options::colours::ConfigColours, utils::error};
|
||||||
|
|
||||||
pub struct CanvasStyling {
|
pub struct CanvasStyling {
|
||||||
@ -124,7 +124,7 @@ macro_rules! try_set_colour_list {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl CanvasStyling {
|
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();
|
let mut canvas_colours = Self::default();
|
||||||
|
|
||||||
match colour_scheme {
|
match colour_scheme {
|
||||||
@ -236,7 +236,7 @@ mod test {
|
|||||||
use tui::style::{Color, Style};
|
use tui::style::{Color, Style};
|
||||||
|
|
||||||
use super::{CanvasStyling, ColourScheme};
|
use super::{CanvasStyling, ColourScheme};
|
||||||
use crate::options::Config;
|
use crate::options::ConfigV1;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn default_selected_colour_works() {
|
fn default_selected_colour_works() {
|
||||||
@ -282,7 +282,7 @@ mod test {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn built_in_colour_schemes_work() {
|
fn built_in_colour_schemes_work() {
|
||||||
let config = Config::default();
|
let config = ConfigV1::default();
|
||||||
CanvasStyling::new(ColourScheme::Default, &config).unwrap();
|
CanvasStyling::new(ColourScheme::Default, &config).unwrap();
|
||||||
CanvasStyling::new(ColourScheme::DefaultLight, &config).unwrap();
|
CanvasStyling::new(ColourScheme::DefaultLight, &config).unwrap();
|
||||||
CanvasStyling::new(ColourScheme::Gruvbox, &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"
|
# Built-in themes. Valid values are "default", "default-light", "gruvbox", "gruvbox-light", "nord", "nord-light"
|
||||||
#color = "default"
|
#color = "default"
|
||||||
# Show memory values in the processes widget as values by 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.
|
# Show tree mode by default in the processes widget.
|
||||||
#tree = false
|
#tree = false
|
||||||
# Shows an indicator in table widgets tracking where in the list you are.
|
# 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;
|
use crate::app::filter::Filter;
|
||||||
|
|
||||||
#[derive(Default, Debug, Clone)]
|
#[derive(Default, Debug, Clone)]
|
||||||
@ -29,6 +31,21 @@ pub enum TemperatureType {
|
|||||||
Fahrenheit,
|
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 {
|
impl TemperatureType {
|
||||||
/// Given a temperature in Celsius, covert it if necessary for a different unit.
|
/// Given a temperature in Celsius, covert it if necessary for a different unit.
|
||||||
pub fn convert_temp_unit(&self, temp_celsius: f32) -> f32 {
|
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,
|
fs,
|
||||||
io::{stderr, stdout, Write},
|
io::{stderr, stdout, Write},
|
||||||
panic::PanicInfo,
|
panic::PanicInfo,
|
||||||
path::PathBuf,
|
path::{Path, PathBuf},
|
||||||
sync::{
|
sync::{
|
||||||
mpsc::{Receiver, Sender},
|
mpsc::{Receiver, Sender},
|
||||||
Arc, Condvar, Mutex,
|
Arc, Condvar, Mutex,
|
||||||
@ -61,7 +61,7 @@ use crossterm::{
|
|||||||
};
|
};
|
||||||
use data_conversion::*;
|
use data_conversion::*;
|
||||||
pub use options::args;
|
pub use options::args;
|
||||||
use options::Config;
|
use options::ConfigV1;
|
||||||
use utils::error;
|
use utils::error;
|
||||||
#[allow(unused_imports)]
|
#[allow(unused_imports)]
|
||||||
pub use utils::logging::*;
|
pub use utils::logging::*;
|
||||||
@ -202,9 +202,9 @@ pub fn handle_key_event_or_break(
|
|||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read_config(config_location: Option<&String>) -> error::Result<Option<PathBuf>> {
|
pub fn get_config_path(override_config_path: Option<&Path>) -> Option<PathBuf> {
|
||||||
let config_path = if let Some(conf_loc) = config_location {
|
if let Some(conf_loc) = override_config_path {
|
||||||
Some(PathBuf::from(conf_loc.as_str()))
|
Some(conf_loc.to_path_buf())
|
||||||
} else if cfg!(target_os = "windows") {
|
} else if cfg!(target_os = "windows") {
|
||||||
if let Some(home_path) = dirs::config_dir() {
|
if let Some(home_path) = dirs::config_dir() {
|
||||||
let mut path = home_path;
|
let mut path = home_path;
|
||||||
@ -232,13 +232,13 @@ pub fn read_config(config_location: Option<&String>) -> error::Result<Option<Pat
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
}
|
||||||
|
|
||||||
Ok(config_path)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_or_get_config(config_path: &Option<PathBuf>) -> error::Result<Config> {
|
pub fn get_or_create_config(override_config_path: Option<&Path>) -> error::Result<ConfigV1> {
|
||||||
if let Some(path) = config_path {
|
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) {
|
if let Ok(config_string) = fs::read_to_string(path) {
|
||||||
Ok(toml_edit::de::from_str(config_string.as_str())?)
|
Ok(toml_edit::de::from_str(config_string.as_str())?)
|
||||||
} else {
|
} 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())?;
|
fs::File::create(path)?.write_all(CONFIG_TEXT.as_bytes())?;
|
||||||
Ok(Config::default())
|
Ok(ConfigV1::default())
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Don't write...
|
// If we somehow don't have any config path, then just assume the default config but don't write to any file.
|
||||||
Ok(Config::default())
|
Ok(ConfigV1::default())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
397
src/options.rs
397
src/options.rs
@ -13,16 +13,18 @@ use std::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use anyhow::{Context, Result};
|
use anyhow::{Context, Result};
|
||||||
use clap::ArgMatches;
|
|
||||||
pub use colours::ConfigColours;
|
pub use colours::ConfigColours;
|
||||||
pub use config::Config;
|
pub use config::ConfigV1;
|
||||||
use hashbrown::{HashMap, HashSet};
|
use hashbrown::{HashMap, HashSet};
|
||||||
use indexmap::IndexSet;
|
use indexmap::IndexSet;
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
#[cfg(feature = "battery")]
|
#[cfg(feature = "battery")]
|
||||||
use starship_battery::Manager;
|
use starship_battery::Manager;
|
||||||
|
|
||||||
use self::config::{layout::Row, IgnoreList, StringOrNum};
|
use self::{
|
||||||
|
args::BottomArgs,
|
||||||
|
config::{layout::Row, IgnoreList, StringOrNum},
|
||||||
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
app::{filter::Filter, layout_manager::*, *},
|
app::{filter::Filter, layout_manager::*, *},
|
||||||
canvas::{components::time_chart::LegendPosition, styling::CanvasStyling, ColourScheme},
|
canvas::{components::time_chart::LegendPosition, styling::CanvasStyling, ColourScheme},
|
||||||
@ -36,8 +38,8 @@ use crate::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
macro_rules! is_flag_enabled {
|
macro_rules! is_flag_enabled {
|
||||||
($flag_name:ident, $matches:expr, $config:expr) => {
|
($flag_name:ident, $arg:expr, $config:expr) => {
|
||||||
if $matches.get_flag(stringify!($flag_name)) {
|
if $arg.$flag_name {
|
||||||
true
|
true
|
||||||
} else if let Some(flags) = &$config.flags {
|
} else if let Some(flags) = &$config.flags {
|
||||||
flags.$flag_name.unwrap_or(false)
|
flags.$flag_name.unwrap_or(false)
|
||||||
@ -58,29 +60,34 @@ macro_rules! is_flag_enabled {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn init_app(
|
pub fn init_app(
|
||||||
matches: ArgMatches, config: Config, widget_layout: &BottomLayout, default_widget_id: u64,
|
args: BottomArgs, config: ConfigV1, styling: &CanvasStyling,
|
||||||
default_widget_type_option: &Option<BottomWidgetType>, styling: &CanvasStyling,
|
) -> Result<(App, BottomLayout)> {
|
||||||
) -> Result<App> {
|
|
||||||
use BottomWidgetType::*;
|
use BottomWidgetType::*;
|
||||||
|
|
||||||
// Since everything takes a reference, but we want to take ownership here to drop matches/config later...
|
// 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 config = &config;
|
||||||
|
|
||||||
let retention_ms =
|
let (widget_layout, default_widget_id, default_widget_type_option) =
|
||||||
get_retention(matches, config).context("Update `retention` in your config file.")?;
|
get_widget_layout(args, config)
|
||||||
let autohide_time = is_flag_enabled!(autohide_time, matches, config);
|
.context("Found an issue while trying to build the widget layout.")?;
|
||||||
let default_time_value = get_default_time_value(matches, config, retention_ms)
|
|
||||||
.context("Update 'default_time_value' in your config file.")?;
|
|
||||||
|
|
||||||
let use_basic_mode = is_flag_enabled!(basic, matches, config);
|
let retention_ms = get_retention(args, config)?;
|
||||||
let expanded = is_flag_enabled!(expanded, matches, 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
|
// For processes
|
||||||
let is_grouped = is_flag_enabled!(group_processes, matches, config);
|
let is_grouped = is_flag_enabled!(group_processes, args.process, config);
|
||||||
let is_case_sensitive = is_flag_enabled!(case_sensitive, matches, config);
|
let is_case_sensitive = is_flag_enabled!(case_sensitive, args.process, config);
|
||||||
let is_match_whole_word = is_flag_enabled!(whole_word, matches, config);
|
let is_match_whole_word = is_flag_enabled!(whole_word, args.process, config);
|
||||||
let is_use_regex = is_flag_enabled!(regex, matches, 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 widget_map = HashMap::new();
|
||||||
let mut cpu_state_map: HashMap<u64, CpuWidgetState> = 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 is_custom_layout = config.row.is_some();
|
||||||
let mut used_widget_set = HashSet::new();
|
let mut used_widget_set = HashSet::new();
|
||||||
|
|
||||||
let show_memory_as_values = is_flag_enabled!(mem_as_value, matches, config);
|
let network_unit_type = get_network_unit_type(args, config);
|
||||||
let is_default_tree = is_flag_enabled!(tree, matches, config);
|
let network_scale_type = get_network_scale_type(args, config);
|
||||||
let is_default_command = is_flag_enabled!(process_command, matches, config);
|
let network_use_binary_prefix =
|
||||||
let is_advanced_kill = !(is_flag_enabled!(disable_advanced_kill, matches, config));
|
is_flag_enabled!(network_use_binary_prefix, args.network, 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 proc_columns: Option<IndexSet<ProcWidgetColumn>> = {
|
let proc_columns: Option<IndexSet<ProcWidgetColumn>> = {
|
||||||
let columns = config.processes.as_ref().map(|cfg| cfg.columns.clone());
|
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 network_legend_position = get_network_legend_position(args, config)?;
|
||||||
let memory_legend_position = get_memory_legend(matches, config)?;
|
let memory_legend_position = get_memory_legend_position(args, config)?;
|
||||||
|
|
||||||
// TODO: Can probably just reuse the options struct.
|
// TODO: Can probably just reuse the options struct.
|
||||||
let app_config_fields = AppConfigFields {
|
let app_config_fields = AppConfigFields {
|
||||||
update_rate: get_update_rate(matches, config)
|
update_rate: get_update_rate(args, config)?,
|
||||||
.context("Update 'rate' in your config file.")?,
|
temperature_type: get_temperature(args, config)
|
||||||
temperature_type: get_temperature(matches, config)
|
|
||||||
.context("Update 'temperature_type' in your config file.")?,
|
.context("Update 'temperature_type' in your config file.")?,
|
||||||
show_average_cpu: get_show_average_cpu(matches, config),
|
show_average_cpu: get_show_average_cpu(args, config),
|
||||||
use_dot: is_flag_enabled!(dot_marker, matches, config),
|
use_dot: is_flag_enabled!(dot_marker, args.general, config),
|
||||||
cpu_left_legend: is_flag_enabled!(cpu_left_legend, matches, config),
|
cpu_left_legend: is_flag_enabled!(cpu_left_legend, args.cpu, config),
|
||||||
use_current_cpu_total: is_flag_enabled!(current_usage, matches, config),
|
use_current_cpu_total: is_flag_enabled!(current_usage, args.process, config),
|
||||||
unnormalized_cpu: is_flag_enabled!(unnormalized_cpu, matches, config),
|
unnormalized_cpu: is_flag_enabled!(unnormalized_cpu, args.process, config),
|
||||||
use_basic_mode,
|
use_basic_mode,
|
||||||
default_time_value,
|
default_time_value,
|
||||||
time_interval: get_time_interval(matches, config, retention_ms)
|
time_interval: get_time_interval(args, config, retention_ms)?,
|
||||||
.context("Update 'time_delta' in your config file.")?,
|
hide_time: is_flag_enabled!(hide_time, args.general, config),
|
||||||
hide_time: is_flag_enabled!(hide_time, matches, config),
|
|
||||||
autohide_time,
|
autohide_time,
|
||||||
use_old_network_legend: is_flag_enabled!(use_old_network_legend, 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, matches, config))),
|
table_gap: u16::from(!(is_flag_enabled!(hide_table_gap, args.general, config))),
|
||||||
disable_click: is_flag_enabled!(disable_click, matches, config),
|
disable_click: is_flag_enabled!(disable_click, args.general, config),
|
||||||
enable_gpu: get_enable_gpu(matches, config),
|
enable_gpu: get_enable_gpu(args, config),
|
||||||
enable_cache_memory: get_enable_cache_memory(matches, config),
|
enable_cache_memory: get_enable_cache_memory(args, config),
|
||||||
show_table_scroll_position: is_flag_enabled!(show_table_scroll_position, matches, config),
|
show_table_scroll_position: is_flag_enabled!(
|
||||||
|
show_table_scroll_position,
|
||||||
|
args.general,
|
||||||
|
config
|
||||||
|
),
|
||||||
is_advanced_kill,
|
is_advanced_kill,
|
||||||
memory_legend_position,
|
memory_legend_position,
|
||||||
network_legend_position,
|
network_legend_position,
|
||||||
@ -165,7 +170,7 @@ pub fn init_app(
|
|||||||
is_case_sensitive,
|
is_case_sensitive,
|
||||||
is_match_whole_word,
|
is_match_whole_word,
|
||||||
is_use_regex,
|
is_use_regex,
|
||||||
show_memory_as_values,
|
show_memory_as_values: process_memory_as_value,
|
||||||
is_command: is_default_command,
|
is_command: is_default_command,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -310,8 +315,8 @@ pub fn init_app(
|
|||||||
let used_widgets = UsedWidgets {
|
let used_widgets = UsedWidgets {
|
||||||
use_cpu: used_widget_set.get(&Cpu).is_some() || used_widget_set.get(&BasicCpu).is_some(),
|
use_cpu: used_widget_set.get(&Cpu).is_some() || used_widget_set.get(&BasicCpu).is_some(),
|
||||||
use_mem,
|
use_mem,
|
||||||
use_cache: use_mem && get_enable_cache_memory(matches, config),
|
use_cache: use_mem && get_enable_cache_memory(args, config),
|
||||||
use_gpu: get_enable_gpu(matches, 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_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_proc: used_widget_set.get(&Proc).is_some(),
|
||||||
use_disk: used_widget_set.get(&Disk).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;
|
let is_expanded = expanded && !use_basic_mode;
|
||||||
|
|
||||||
Ok(App::new(
|
Ok((
|
||||||
app_config_fields,
|
App::new(
|
||||||
states,
|
app_config_fields,
|
||||||
widget_map,
|
states,
|
||||||
current_widget,
|
widget_map,
|
||||||
used_widgets,
|
current_widget,
|
||||||
filters,
|
used_widgets,
|
||||||
is_expanded,
|
filters,
|
||||||
|
is_expanded,
|
||||||
|
),
|
||||||
|
widget_layout,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_widget_layout(
|
pub fn get_widget_layout(
|
||||||
matches: &ArgMatches, config: &Config,
|
args: &BottomArgs, config: &ConfigV1,
|
||||||
) -> error::Result<(BottomLayout, u64, Option<BottomWidgetType>)> {
|
) -> 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) =
|
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 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;
|
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 {
|
} else {
|
||||||
let ref_row: Vec<Row>; // Required to handle reference
|
let ref_row: Vec<Row>; // Required to handle reference
|
||||||
let rows = match &config.row {
|
let rows = match &config.row {
|
||||||
Some(r) => r,
|
Some(r) => r,
|
||||||
None => {
|
None => {
|
||||||
// This cannot (like it really shouldn't) fail!
|
// 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
|
DEFAULT_BATTERY_LAYOUT
|
||||||
} else {
|
} else {
|
||||||
DEFAULT_LAYOUT
|
DEFAULT_LAYOUT
|
||||||
@ -423,13 +431,17 @@ pub fn get_widget_layout(
|
|||||||
Ok((bottom_layout, default_widget_id, default_widget_type))
|
Ok((bottom_layout, default_widget_id, default_widget_type))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_update_rate(matches: &ArgMatches, config: &Config) -> error::Result<u64> {
|
fn get_update_rate(args: &BottomArgs, config: &ConfigV1) -> error::Result<u64> {
|
||||||
let update_rate = if let Some(update_rate) = matches.get_one::<String>("rate") {
|
let update_rate = if let Some(update_rate) = &args.general.rate {
|
||||||
try_parse_ms(update_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 {
|
} else if let Some(flags) = &config.flags {
|
||||||
if let Some(rate) = &flags.rate {
|
if let Some(rate) = &flags.rate {
|
||||||
match 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,
|
StringOrNum::Num(n) => *n,
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -448,32 +460,24 @@ fn get_update_rate(matches: &ArgMatches, config: &Config) -> error::Result<u64>
|
|||||||
Ok(update_rate)
|
Ok(update_rate)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_temperature(matches: &ArgMatches, config: &Config) -> error::Result<TemperatureType> {
|
fn get_temperature(args: &BottomArgs, config: &ConfigV1) -> error::Result<TemperatureType> {
|
||||||
if matches.get_flag("fahrenheit") {
|
if args.temperature.fahrenheit {
|
||||||
return Ok(TemperatureType::Fahrenheit);
|
return Ok(TemperatureType::Fahrenheit);
|
||||||
} else if matches.get_flag("kelvin") {
|
} else if args.temperature.kelvin {
|
||||||
return Ok(TemperatureType::Kelvin);
|
return Ok(TemperatureType::Kelvin);
|
||||||
} else if matches.get_flag("celsius") {
|
} else if args.temperature.celsius {
|
||||||
return Ok(TemperatureType::Celsius);
|
return Ok(TemperatureType::Celsius);
|
||||||
} else if let Some(flags) = &config.flags {
|
} else if let Some(flags) = &config.flags {
|
||||||
if let Some(temp_type) = &flags.temperature_type {
|
if let Some(temp_type) = &flags.temperature_type {
|
||||||
// Give lowest priority to config.
|
return TemperatureType::from_str(temp_type).map_err(BottomError::ConfigError);
|
||||||
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>\"."
|
|
||||||
))),
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(TemperatureType::Celsius)
|
Ok(TemperatureType::Celsius)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Yes, this function gets whether to show average CPU (true) or not (false)
|
/// Yes, this function gets whether to show average CPU (true) or not (false).
|
||||||
fn get_show_average_cpu(matches: &ArgMatches, config: &Config) -> bool {
|
fn get_show_average_cpu(args: &BottomArgs, config: &ConfigV1) -> bool {
|
||||||
if matches.get_flag("hide_avg_cpu") {
|
if args.cpu.hide_avg_cpu {
|
||||||
return false;
|
return false;
|
||||||
} else if let Some(flags) = &config.flags {
|
} else if let Some(flags) = &config.flags {
|
||||||
if let Some(avg_cpu) = flags.hide_avg_cpu {
|
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(
|
fn get_default_time_value(
|
||||||
matches: &ArgMatches, config: &Config, retention_ms: u64,
|
args: &BottomArgs, config: &ConfigV1, retention_ms: u64,
|
||||||
) -> error::Result<u64> {
|
) -> error::Result<u64> {
|
||||||
let default_time =
|
let default_time = if let Some(default_time_value) = &args.general.default_time_value {
|
||||||
if let Some(default_time_value) = matches.get_one::<String>("default_time_value") {
|
try_parse_ms(default_time_value).map_err(|_| {
|
||||||
try_parse_ms(default_time_value)?
|
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 {
|
} else if let Some(flags) = &config.flags {
|
||||||
match default_time_value {
|
if let Some(default_time_value) = &flags.default_time_value {
|
||||||
StringOrNum::String(s) => try_parse_ms(s)?,
|
match default_time_value {
|
||||||
StringOrNum::Num(n) => *n,
|
StringOrNum::String(s) => try_parse_ms(s).map_err(|_| {
|
||||||
}
|
BottomError::ConfigError("set your default time to be valid".to_string())
|
||||||
} else {
|
})?,
|
||||||
DEFAULT_TIME_MILLISECONDS
|
StringOrNum::Num(n) => *n,
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
DEFAULT_TIME_MILLISECONDS
|
DEFAULT_TIME_MILLISECONDS
|
||||||
};
|
}
|
||||||
|
} else {
|
||||||
|
DEFAULT_TIME_MILLISECONDS
|
||||||
|
};
|
||||||
|
|
||||||
if default_time < 30000 {
|
if default_time < 30000 {
|
||||||
return Err(BottomError::ConfigError(
|
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 {
|
} else if default_time > retention_ms {
|
||||||
return Err(BottomError::ConfigError(format!(
|
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))
|
humantime::Duration::from(Duration::from_millis(retention_ms))
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
@ -530,14 +537,18 @@ fn get_default_time_value(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn get_time_interval(
|
fn get_time_interval(
|
||||||
matches: &ArgMatches, config: &Config, retention_ms: u64,
|
args: &BottomArgs, config: &ConfigV1, retention_ms: u64,
|
||||||
) -> error::Result<u64> {
|
) -> error::Result<u64> {
|
||||||
let time_interval = if let Some(time_interval) = matches.get_one::<String>("time_delta") {
|
let time_interval = if let Some(time_interval) = &args.general.time_delta {
|
||||||
try_parse_ms(time_interval)?
|
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 {
|
} else if let Some(flags) = &config.flags {
|
||||||
if let Some(time_interval) = &flags.time_delta {
|
if let Some(time_interval) = &flags.time_delta {
|
||||||
match time_interval {
|
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,
|
StringOrNum::Num(n) => *n,
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -562,9 +573,9 @@ fn get_time_interval(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn get_default_widget_and_count(
|
fn get_default_widget_and_count(
|
||||||
matches: &ArgMatches, config: &Config,
|
args: &BottomArgs, config: &ConfigV1,
|
||||||
) -> error::Result<(Option<BottomWidgetType>, u64)> {
|
) -> 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>()?;
|
let parsed_widget = widget_type.parse::<BottomWidgetType>()?;
|
||||||
if let BottomWidgetType::Empty = parsed_widget {
|
if let BottomWidgetType::Empty = parsed_widget {
|
||||||
None
|
None
|
||||||
@ -586,21 +597,20 @@ fn get_default_widget_and_count(
|
|||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
let widget_count = if let Some(widget_count) = matches.get_one::<String>("default_widget_count")
|
let widget_count: Option<u128> = if let Some(widget_count) = args.general.default_widget_count {
|
||||||
{
|
Some(widget_count.into())
|
||||||
Some(widget_count.parse::<u128>()?)
|
|
||||||
} else if let Some(flags) = &config.flags {
|
|
||||||
flags
|
|
||||||
.default_widget_count
|
|
||||||
.map(|widget_count| widget_count.into())
|
|
||||||
} else {
|
} else {
|
||||||
None
|
config.flags.as_ref().and_then(|flags| {
|
||||||
|
flags
|
||||||
|
.default_widget_count
|
||||||
|
.map(|widget_count| widget_count.into())
|
||||||
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
match (widget_type, widget_count) {
|
match (widget_type, widget_count) {
|
||||||
(Some(widget_type), Some(widget_count)) => {
|
(Some(widget_type), Some(widget_count)) => {
|
||||||
let widget_count = widget_count.try_into().map_err(|_| BottomError::ConfigError(
|
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))
|
Ok((Some(widget_type), widget_count))
|
||||||
}
|
}
|
||||||
@ -613,9 +623,10 @@ fn get_default_widget_and_count(
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[allow(unused_variables)]
|
#[allow(unused_variables)]
|
||||||
fn get_use_battery(matches: &ArgMatches, config: &Config) -> bool {
|
fn get_use_battery(args: &BottomArgs, config: &ConfigV1) -> bool {
|
||||||
#[cfg(feature = "battery")]
|
#[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(battery_manager) = Manager::new() {
|
||||||
if let Ok(batteries) = battery_manager.batteries() {
|
if let Ok(batteries) = battery_manager.batteries() {
|
||||||
if batteries.count() == 0 {
|
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;
|
return true;
|
||||||
} else if let Some(flags) = &config.flags {
|
} else if let Some(flags) = &config.flags {
|
||||||
if let Some(battery) = flags.battery {
|
if let Some(battery) = flags.battery {
|
||||||
@ -637,10 +648,10 @@ fn get_use_battery(matches: &ArgMatches, config: &Config) -> bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[allow(unused_variables)]
|
#[allow(unused_variables)]
|
||||||
fn get_enable_gpu(matches: &ArgMatches, config: &Config) -> bool {
|
fn get_enable_gpu(args: &BottomArgs, config: &ConfigV1) -> bool {
|
||||||
#[cfg(feature = "gpu")]
|
#[cfg(feature = "gpu")]
|
||||||
{
|
{
|
||||||
if matches.get_flag("enable_gpu") {
|
if args.gpu.enable_gpu {
|
||||||
return true;
|
return true;
|
||||||
} else if let Some(flags) = &config.flags {
|
} else if let Some(flags) = &config.flags {
|
||||||
if let Some(enable_gpu) = flags.enable_gpu {
|
if let Some(enable_gpu) = flags.enable_gpu {
|
||||||
@ -653,10 +664,10 @@ fn get_enable_gpu(matches: &ArgMatches, config: &Config) -> bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[allow(unused_variables)]
|
#[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"))]
|
#[cfg(not(target_os = "windows"))]
|
||||||
{
|
{
|
||||||
if matches.get_flag("enable_cache_memory") {
|
if args.memory.enable_cache_memory {
|
||||||
return true;
|
return true;
|
||||||
} else if let Some(flags) = &config.flags {
|
} else if let Some(flags) = &config.flags {
|
||||||
if let Some(enable_cache_memory) = flags.enable_cache_memory {
|
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> {
|
pub fn get_color_scheme(args: &BottomArgs, config: &ConfigV1) -> error::Result<ColourScheme> {
|
||||||
if let Some(color) = matches.get_one::<String>("color") {
|
if let Some(color) = &args.style.color {
|
||||||
// Highest priority is always command line flags...
|
// Highest priority is always command line flags...
|
||||||
return ColourScheme::from_str(color);
|
return ColourScheme::from_str(color);
|
||||||
} else if let Some(colors) = &config.colors {
|
} 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)
|
Ok(ColourScheme::Default)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_network_unit_type(matches: &ArgMatches, config: &Config) -> DataUnit {
|
fn get_network_unit_type(args: &BottomArgs, config: &ConfigV1) -> DataUnit {
|
||||||
if matches.get_flag("network_use_bytes") {
|
if args.network.network_use_bytes {
|
||||||
return DataUnit::Byte;
|
return DataUnit::Byte;
|
||||||
} else if let Some(flags) = &config.flags {
|
} else if let Some(flags) = &config.flags {
|
||||||
if let Some(network_use_bytes) = flags.network_use_bytes {
|
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
|
DataUnit::Bit
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_network_scale_type(matches: &ArgMatches, config: &Config) -> AxisScaling {
|
fn get_network_scale_type(args: &BottomArgs, config: &ConfigV1) -> AxisScaling {
|
||||||
if matches.get_flag("network_use_log") {
|
if args.network.network_use_log {
|
||||||
return AxisScaling::Log;
|
return AxisScaling::Log;
|
||||||
} else if let Some(flags) = &config.flags {
|
} else if let Some(flags) = &config.flags {
|
||||||
if let Some(network_use_log) = flags.network_use_log {
|
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
|
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.
|
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)
|
try_parse_ms(retention)
|
||||||
|
.map_err(|_| BottomError::ArgumentError("`retention` is an invalid value".to_string()))
|
||||||
} else if let Some(flags) = &config.flags {
|
} else if let Some(flags) = &config.flags {
|
||||||
if let Some(retention) = &flags.retention {
|
if let Some(retention) = &flags.retention {
|
||||||
Ok(match 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,
|
StringOrNum::Num(n) => *n,
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
@ -777,19 +791,21 @@ fn get_retention(matches: &ArgMatches, config: &Config) -> error::Result<u64> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_network_legend(
|
fn get_network_legend_position(
|
||||||
matches: &ArgMatches, config: &Config,
|
args: &BottomArgs, config: &ConfigV1,
|
||||||
) -> error::Result<Option<LegendPosition>> {
|
) -> error::Result<Option<LegendPosition>> {
|
||||||
let error =
|
if let Some(s) = &args.network.network_legend {
|
||||||
|_| BottomError::ConfigError("network_legend is set to an invalid value".to_string());
|
|
||||||
if let Some(s) = matches.get_one::<String>("network_legend") {
|
|
||||||
match s.to_ascii_lowercase().trim() {
|
match s.to_ascii_lowercase().trim() {
|
||||||
"none" => Ok(None),
|
"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 {
|
} else if let Some(flags) = &config.flags {
|
||||||
if let Some(legend) = &flags.network_legend {
|
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 {
|
} else {
|
||||||
Ok(Some(LegendPosition::default()))
|
Ok(Some(LegendPosition::default()))
|
||||||
}
|
}
|
||||||
@ -798,19 +814,21 @@ fn get_network_legend(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_memory_legend(
|
fn get_memory_legend_position(
|
||||||
matches: &ArgMatches, config: &Config,
|
args: &BottomArgs, config: &ConfigV1,
|
||||||
) -> error::Result<Option<LegendPosition>> {
|
) -> error::Result<Option<LegendPosition>> {
|
||||||
let error =
|
if let Some(s) = &args.memory.memory_legend {
|
||||||
|_| BottomError::ConfigError("memory_legend is set to an invalid value".to_string());
|
|
||||||
if let Some(s) = matches.get_one::<String>("memory_legend") {
|
|
||||||
match s.to_ascii_lowercase().trim() {
|
match s.to_ascii_lowercase().trim() {
|
||||||
"none" => Ok(None),
|
"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 {
|
} else if let Some(flags) = &config.flags {
|
||||||
if let Some(legend) = &flags.memory_legend {
|
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 {
|
} else {
|
||||||
Ok(Some(LegendPosition::default()))
|
Ok(Some(LegendPosition::default()))
|
||||||
}
|
}
|
||||||
@ -821,11 +839,12 @@ fn get_memory_legend(
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod 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::{
|
use crate::{
|
||||||
app::App,
|
app::App,
|
||||||
|
args::BottomArgs,
|
||||||
canvas::styling::CanvasStyling,
|
canvas::styling::CanvasStyling,
|
||||||
options::{
|
options::{
|
||||||
config::ConfigFlags, get_default_time_value, get_retention, get_update_rate,
|
config::ConfigFlags, get_default_time_value, get_retention, get_update_rate,
|
||||||
@ -854,26 +873,24 @@ mod test {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn matches_human_times() {
|
fn matches_human_times() {
|
||||||
let config = Config::default();
|
let config = ConfigV1::default();
|
||||||
let app = crate::args::build_app();
|
|
||||||
|
|
||||||
{
|
{
|
||||||
let app = app.clone();
|
|
||||||
let delta_args = vec!["btm", "--time_delta", "2 min"];
|
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!(
|
assert_eq!(
|
||||||
get_time_interval(&matches, &config, 60 * 60 * 1000),
|
get_time_interval(&args, &config, 60 * 60 * 1000),
|
||||||
Ok(2 * 60 * 1000)
|
Ok(2 * 60 * 1000)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
let default_time_args = vec!["btm", "--default_time_value", "300s"];
|
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!(
|
assert_eq!(
|
||||||
get_default_time_value(&matches, &config, 60 * 60 * 1000),
|
get_default_time_value(&args, &config, 60 * 60 * 1000),
|
||||||
Ok(5 * 60 * 1000)
|
Ok(5 * 60 * 1000)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -881,26 +898,24 @@ mod test {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn matches_number_times() {
|
fn matches_number_times() {
|
||||||
let config = Config::default();
|
let config = ConfigV1::default();
|
||||||
let app = crate::args::build_app();
|
|
||||||
|
|
||||||
{
|
{
|
||||||
let app = app.clone();
|
|
||||||
let delta_args = vec!["btm", "--time_delta", "120000"];
|
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!(
|
assert_eq!(
|
||||||
get_time_interval(&matches, &config, 60 * 60 * 1000),
|
get_time_interval(&args, &config, 60 * 60 * 1000),
|
||||||
Ok(2 * 60 * 1000)
|
Ok(2 * 60 * 1000)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
let default_time_args = vec!["btm", "--default_time_value", "300000"];
|
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!(
|
assert_eq!(
|
||||||
get_default_time_value(&matches, &config, 60 * 60 * 1000),
|
get_default_time_value(&args, &config, 60 * 60 * 1000),
|
||||||
Ok(5 * 60 * 1000)
|
Ok(5 * 60 * 1000)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -908,10 +923,9 @@ mod test {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn config_human_times() {
|
fn config_human_times() {
|
||||||
let app = crate::args::build_app();
|
let args = BottomArgs::parse_from(["btm"]);
|
||||||
let matches = app.get_matches_from(["btm"]);
|
|
||||||
|
|
||||||
let mut config = Config::default();
|
let mut config = ConfigV1::default();
|
||||||
let flags = ConfigFlags {
|
let flags = ConfigFlags {
|
||||||
time_delta: Some("2 min".to_string().into()),
|
time_delta: Some("2 min".to_string().into()),
|
||||||
default_time_value: Some("300s".to_string().into()),
|
default_time_value: Some("300s".to_string().into()),
|
||||||
@ -923,26 +937,25 @@ mod test {
|
|||||||
config.flags = Some(flags);
|
config.flags = Some(flags);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
get_time_interval(&matches, &config, 60 * 60 * 1000),
|
get_time_interval(&args, &config, 60 * 60 * 1000),
|
||||||
Ok(2 * 60 * 1000)
|
Ok(2 * 60 * 1000)
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
get_default_time_value(&matches, &config, 60 * 60 * 1000),
|
get_default_time_value(&args, &config, 60 * 60 * 1000),
|
||||||
Ok(5 * 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]
|
#[test]
|
||||||
fn config_number_times_as_string() {
|
fn config_number_times_as_string() {
|
||||||
let app = crate::args::build_app();
|
let args = BottomArgs::parse_from(["btm"]);
|
||||||
let matches = app.get_matches_from(["btm"]);
|
|
||||||
|
|
||||||
let mut config = Config::default();
|
let mut config = ConfigV1::default();
|
||||||
let flags = ConfigFlags {
|
let flags = ConfigFlags {
|
||||||
time_delta: Some("120000".to_string().into()),
|
time_delta: Some("120000".to_string().into()),
|
||||||
default_time_value: Some("300000".to_string().into()),
|
default_time_value: Some("300000".to_string().into()),
|
||||||
@ -954,26 +967,25 @@ mod test {
|
|||||||
config.flags = Some(flags);
|
config.flags = Some(flags);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
get_time_interval(&matches, &config, 60 * 60 * 1000),
|
get_time_interval(&args, &config, 60 * 60 * 1000),
|
||||||
Ok(2 * 60 * 1000)
|
Ok(2 * 60 * 1000)
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
get_default_time_value(&matches, &config, 60 * 60 * 1000),
|
get_default_time_value(&args, &config, 60 * 60 * 1000),
|
||||||
Ok(5 * 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]
|
#[test]
|
||||||
fn config_number_times_as_num() {
|
fn config_number_times_as_num() {
|
||||||
let app = crate::args::build_app();
|
let args = BottomArgs::parse_from(["btm"]);
|
||||||
let matches = app.get_matches_from(["btm"]);
|
|
||||||
|
|
||||||
let mut config = Config::default();
|
let mut config = ConfigV1::default();
|
||||||
let flags = ConfigFlags {
|
let flags = ConfigFlags {
|
||||||
time_delta: Some(120000.into()),
|
time_delta: Some(120000.into()),
|
||||||
default_time_value: Some(300000.into()),
|
default_time_value: Some(300000.into()),
|
||||||
@ -985,41 +997,35 @@ mod test {
|
|||||||
config.flags = Some(flags);
|
config.flags = Some(flags);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
get_time_interval(&matches, &config, 60 * 60 * 1000),
|
get_time_interval(&args, &config, 60 * 60 * 1000),
|
||||||
Ok(2 * 60 * 1000)
|
Ok(2 * 60 * 1000)
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
get_default_time_value(&matches, &config, 60 * 60 * 1000),
|
get_default_time_value(&args, &config, 60 * 60 * 1000),
|
||||||
Ok(5 * 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 {
|
fn create_app(args: BottomArgs) -> App {
|
||||||
let (layout, id, ty) = get_widget_layout(&matches, &config).unwrap();
|
let config = ConfigV1::default();
|
||||||
let styling =
|
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
|
// 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?
|
// typos/mixing up. Use proc macros to unify on one struct?
|
||||||
#[test]
|
#[test]
|
||||||
fn verify_cli_options_build() {
|
fn verify_cli_options_build() {
|
||||||
let app = crate::args::build_app();
|
let app = crate::args::build_cmd();
|
||||||
|
|
||||||
let default_app = {
|
let default_app = create_app(BottomArgs::parse_from(["btm"]));
|
||||||
let app = app.clone();
|
|
||||||
let config = Config::default();
|
|
||||||
let matches = app.get_matches_from([""]);
|
|
||||||
|
|
||||||
create_app(config, matches)
|
|
||||||
};
|
|
||||||
|
|
||||||
// Skip battery since it's tricky to test depending on the platform/features we're testing with.
|
// Skip battery since it's tricky to test depending on the platform/features we're testing with.
|
||||||
let skip = ["help", "version", "celsius", "battery"];
|
let skip = ["help", "version", "celsius", "battery"];
|
||||||
@ -1036,11 +1042,8 @@ mod test {
|
|||||||
let arg = format!("--{arg_name}");
|
let arg = format!("--{arg_name}");
|
||||||
|
|
||||||
let arguments = vec!["btm", &arg];
|
let arguments = vec!["btm", &arg];
|
||||||
let app = app.clone();
|
let args = BottomArgs::parse_from(arguments);
|
||||||
let config = Config::default();
|
let testing_app = create_app(args);
|
||||||
let matches = app.get_matches_from(arguments);
|
|
||||||
|
|
||||||
let testing_app = create_app(config, matches);
|
|
||||||
|
|
||||||
if (default_app.app_config_fields == testing_app.app_config_fields)
|
if (default_app.app_config_fields == testing_app.app_config_fields)
|
||||||
&& default_app.is_expanded == testing_app.is_expanded
|
&& 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;
|
use super::ConfigColours;
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default, Deserialize)]
|
#[derive(Clone, Debug, Default, Deserialize)]
|
||||||
pub struct Config {
|
pub struct ConfigV1 {
|
||||||
pub(crate) flags: Option<ConfigFlags>,
|
pub(crate) flags: Option<ConfigFlags>,
|
||||||
pub(crate) colors: Option<ConfigColours>,
|
pub(crate) colors: Option<ConfigColours>,
|
||||||
pub(crate) row: Option<Vec<Row>>,
|
pub(crate) row: Option<Vec<Row>>,
|
||||||
@ -71,7 +71,7 @@ pub(crate) struct ConfigFlags {
|
|||||||
pub(crate) memory_legend: Option<String>,
|
pub(crate) memory_legend: Option<String>,
|
||||||
/// For built-in colour palettes.
|
/// For built-in colour palettes.
|
||||||
pub(crate) color: Option<String>,
|
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) tree: Option<bool>,
|
||||||
pub(crate) show_table_scroll_position: Option<bool>,
|
pub(crate) show_table_scroll_position: Option<bool>,
|
||||||
pub(crate) process_command: Option<bool>,
|
pub(crate) process_command: Option<bool>,
|
||||||
|
@ -239,7 +239,7 @@ mod test {
|
|||||||
use super::*;
|
use super::*;
|
||||||
use crate::{
|
use crate::{
|
||||||
constants::{DEFAULT_LAYOUT, DEFAULT_WIDGET_ID},
|
constants::{DEFAULT_LAYOUT, DEFAULT_WIDGET_ID},
|
||||||
options::Config,
|
options::ConfigV1,
|
||||||
utils::error,
|
utils::error,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -293,7 +293,7 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
/// Tests the default setup.
|
/// Tests the default setup.
|
||||||
fn test_default_movement() {
|
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);
|
let ret_bottom_layout = test_create_layout(&rows, DEFAULT_WIDGET_ID, None, 1, false);
|
||||||
|
|
||||||
// Simple tests for the top CPU widget
|
// Simple tests for the top CPU widget
|
||||||
@ -367,7 +367,7 @@ mod test {
|
|||||||
fn test_default_battery_movement() {
|
fn test_default_battery_movement() {
|
||||||
use crate::constants::DEFAULT_BATTERY_LAYOUT;
|
use crate::constants::DEFAULT_BATTERY_LAYOUT;
|
||||||
|
|
||||||
let rows = from_str::<Config>(DEFAULT_BATTERY_LAYOUT)
|
let rows = from_str::<ConfigV1>(DEFAULT_BATTERY_LAYOUT)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.row
|
.row
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@ -413,7 +413,7 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
/// Tests using cpu_left_legend.
|
/// Tests using cpu_left_legend.
|
||||||
fn test_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);
|
let ret_bottom_layout = test_create_layout(&rows, DEFAULT_WIDGET_ID, None, 1, true);
|
||||||
|
|
||||||
// Legend
|
// Legend
|
||||||
@ -473,7 +473,7 @@ mod test {
|
|||||||
type="proc"
|
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 iter_id = 0; // A lazy way of forcing unique IDs *shrugs*
|
||||||
let mut total_height_ratio = 0;
|
let mut total_height_ratio = 0;
|
||||||
let mut default_widget_count = 1;
|
let mut default_widget_count = 1;
|
||||||
@ -506,7 +506,7 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
/// Tests default widget by setting type and count.
|
/// Tests default widget by setting type and count.
|
||||||
fn test_default_widget_by_option() {
|
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 iter_id = 0; // A lazy way of forcing unique IDs *shrugs*
|
||||||
let mut total_height_ratio = 0;
|
let mut total_height_ratio = 0;
|
||||||
let mut default_widget_count = 3;
|
let mut default_widget_count = 3;
|
||||||
@ -538,7 +538,7 @@ mod test {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_proc_custom_layout() {
|
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);
|
let ret_bottom_layout = test_create_layout(&rows, DEFAULT_WIDGET_ID, None, 1, false);
|
||||||
|
|
||||||
// First proc widget
|
// First proc widget
|
||||||
|
@ -17,21 +17,22 @@ pub enum BottomError {
|
|||||||
/// An error to represent generic errors.
|
/// An error to represent generic errors.
|
||||||
#[error("Error, {0}")]
|
#[error("Error, {0}")]
|
||||||
GenericError(String),
|
GenericError(String),
|
||||||
|
#[cfg(feature = "fern")]
|
||||||
/// An error to represent errors with fern.
|
/// An error to represent errors with fern.
|
||||||
#[error("Fern error, {0}")]
|
#[error("Fern error, {0}")]
|
||||||
FernError(String),
|
FernError(String),
|
||||||
|
/// An error to represent invalid command-line arguments.
|
||||||
|
#[error("Invalid argument, {0}")]
|
||||||
|
ArgumentError(String),
|
||||||
/// An error to represent errors with the config.
|
/// An error to represent errors with the config.
|
||||||
#[error("Configuration file error, {0}")]
|
#[error("Configuration file error, {0}")]
|
||||||
ConfigError(String),
|
ConfigError(String),
|
||||||
/// An error to represent errors with converting between data types.
|
/// An error to represent errors with converting between data types.
|
||||||
#[error("Conversion error, {0}")]
|
#[error("Conversion error, {0}")]
|
||||||
ConversionError(String),
|
ConversionError(String),
|
||||||
/// An error to represent errors with querying.
|
/// An error to represent errors with a query.
|
||||||
#[error("Query error, {0}")]
|
#[error("Query error, {0}")]
|
||||||
QueryError(Cow<'static, str>),
|
QueryError(Cow<'static, str>),
|
||||||
/// An error that just signifies something minor went wrong; no message.
|
|
||||||
#[error("Minor error.")]
|
|
||||||
MinorError,
|
|
||||||
#[error("Error casting integers {0}")]
|
#[error("Error casting integers {0}")]
|
||||||
TryFromIntError(#[from] std::num::TryFromIntError),
|
TryFromIntError(#[from] std::num::TryFromIntError),
|
||||||
}
|
}
|
||||||
|
@ -255,7 +255,7 @@ impl ProcWidgetState {
|
|||||||
|
|
||||||
let is_count = matches!(mode, ProcWidgetMode::Grouped);
|
let is_count = matches!(mode, ProcWidgetMode::Grouped);
|
||||||
let is_command = table_config.is_command;
|
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 {
|
match config_columns {
|
||||||
Some(columns) if !columns.is_empty() => columns
|
Some(columns) if !columns.is_empty() => columns
|
||||||
@ -278,7 +278,7 @@ impl ProcWidgetState {
|
|||||||
}
|
}
|
||||||
ProcWidgetColumn::Cpu => CpuPercent,
|
ProcWidgetColumn::Cpu => CpuPercent,
|
||||||
ProcWidgetColumn::Mem => {
|
ProcWidgetColumn::Mem => {
|
||||||
if mem_vals {
|
if mem_as_values {
|
||||||
MemoryVal
|
MemoryVal
|
||||||
} else {
|
} else {
|
||||||
MemoryPercent
|
MemoryPercent
|
||||||
@ -293,7 +293,7 @@ impl ProcWidgetState {
|
|||||||
ProcWidgetColumn::Time => Time,
|
ProcWidgetColumn::Time => Time,
|
||||||
#[cfg(feature = "gpu")]
|
#[cfg(feature = "gpu")]
|
||||||
ProcWidgetColumn::GpuMem => {
|
ProcWidgetColumn::GpuMem => {
|
||||||
if mem_vals {
|
if mem_as_values {
|
||||||
GpuMem
|
GpuMem
|
||||||
} else {
|
} else {
|
||||||
GpuMemPercent
|
GpuMemPercent
|
||||||
@ -311,7 +311,11 @@ impl ProcWidgetState {
|
|||||||
if is_count { Count } else { Pid },
|
if is_count { Count } else { Pid },
|
||||||
if is_command { Command } else { Name },
|
if is_command { Command } else { Name },
|
||||||
CpuPercent,
|
CpuPercent,
|
||||||
if mem_vals { MemoryVal } else { MemoryPercent },
|
if mem_as_values {
|
||||||
|
MemoryVal
|
||||||
|
} else {
|
||||||
|
MemoryPercent
|
||||||
|
},
|
||||||
ReadPerSecond,
|
ReadPerSecond,
|
||||||
WritePerSecond,
|
WritePerSecond,
|
||||||
TotalRead,
|
TotalRead,
|
||||||
|
@ -24,7 +24,9 @@ fn test_large_default_time() {
|
|||||||
.arg("18446744073709551616")
|
.arg("18446744073709551616")
|
||||||
.assert()
|
.assert()
|
||||||
.failure()
|
.failure()
|
||||||
.stderr(predicate::str::contains("could not parse"));
|
.stderr(predicate::str::contains(
|
||||||
|
"set your default time to be valid",
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -35,7 +37,7 @@ fn test_small_default_time() {
|
|||||||
.assert()
|
.assert()
|
||||||
.failure()
|
.failure()
|
||||||
.stderr(predicate::str::contains(
|
.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")
|
.arg("18446744073709551616")
|
||||||
.assert()
|
.assert()
|
||||||
.failure()
|
.failure()
|
||||||
.stderr(predicate::str::contains("could not parse"));
|
.stderr(predicate::str::contains("set your time delta to be valid"));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -68,7 +70,7 @@ fn test_large_rate() {
|
|||||||
.arg("18446744073709551616")
|
.arg("18446744073709551616")
|
||||||
.assert()
|
.assert()
|
||||||
.failure()
|
.failure()
|
||||||
.stderr(predicate::str::contains("could not parse"));
|
.stderr(predicate::str::contains("set your update rate"));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -89,7 +91,7 @@ fn test_invalid_rate() {
|
|||||||
.arg("100-1000")
|
.arg("100-1000")
|
||||||
.assert()
|
.assert()
|
||||||
.failure()
|
.failure()
|
||||||
.stderr(predicate::str::contains("could not parse"));
|
.stderr(predicate::str::contains("set your update rate"));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -121,9 +123,7 @@ fn test_invalid_default_widget_2() {
|
|||||||
.arg("18446744073709551616")
|
.arg("18446744073709551616")
|
||||||
.assert()
|
.assert()
|
||||||
.failure()
|
.failure()
|
||||||
.stderr(predicate::str::contains(
|
.stderr(predicate::str::contains("number too large"));
|
||||||
"set your widget count to be at most unsigned INT_MAX",
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
Loading…
Reference in New Issue
Block a user