diff --git a/CHANGELOG.md b/CHANGELOG.md index c2635ebe..b5b11afe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -32,6 +32,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - [#868](https://github.com/ClementTsang/bottom/pull/868): Make temperature widget sortable. - [#870](https://github.com/ClementTsang/bottom/pull/870): Make disk widget sortable. - [#881](https://github.com/ClementTsang/bottom/pull/881): Add pasting to the search bar. +- [#892](https://github.com/ClementTsang/bottom/pull/892): Add custom retention periods for data. ## [0.6.8] - 2022-02-01 diff --git a/Cargo.lock b/Cargo.lock index b497b1cf..8b50515b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -224,6 +224,8 @@ dependencies = [ "futures-timer", "fxhash", "heim", + "humantime", + "humantime-serde", "indexmap", "itertools", "kstring", @@ -889,6 +891,22 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + +[[package]] +name = "humantime-serde" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57a3db5ea5923d99402c94e9feb261dc5ee9b4efa158b0315f788cf549cc200c" +dependencies = [ + "humantime", + "serde", +] + [[package]] name = "ident_case" version = "1.0.1" diff --git a/Cargo.toml b/Cargo.toml index 72f1ed0e..bc9dd6b3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -73,6 +73,8 @@ flume = "0.10.14" futures = "0.3.21" futures-timer = "3.0.2" fxhash = "0.2.1" +humantime = "2.1.0" +humantime-serde = "1.1.1" indexmap = "1.8.1" itertools = "0.10.3" kstring = { version = "2.0.0", features = ["arc"] } diff --git a/docs/content/configuration/command-line-flags.md b/docs/content/configuration/command-line-flags.md index 3247c482..febf2068 100644 --- a/docs/content/configuration/command-line-flags.md +++ b/docs/content/configuration/command-line-flags.md @@ -44,3 +44,4 @@ The following flags can be provided to bottom in the command line to change the | `-V, --version` | Prints version information. | | `-W, --whole_word` | Enables whole-word matching by default. | | `--enable_gpu_memory` | Enable collecting and displaying GPU memory usage. | +| `--retention` | How much data is stored at once in terms of time. | diff --git a/docs/content/configuration/config-file/flags.md b/docs/content/configuration/config-file/flags.md index 93c71379..5aa218ca 100644 --- a/docs/content/configuration/config-file/flags.md +++ b/docs/content/configuration/config-file/flags.md @@ -37,3 +37,4 @@ Most of the [command line flags](../../command-line-flags) have config file equi | `network_use_bytes` | Boolean | Displays the network widget using bytes. | | `network_use_log` | Boolean | Displays the network widget with a log scale. | | `enable_gpu_memory` | Boolean | Shows the GPU memory widget. | +| `retention` | String (human readable time, such as "10m", "1h", etc.) | How much data is stored at once in terms of time. | diff --git a/sample_configs/default_config.toml b/sample_configs/default_config.toml index 74b30da3..7ead3315 100644 --- a/sample_configs/default_config.toml +++ b/sample_configs/default_config.toml @@ -71,6 +71,8 @@ #disable_advanced_kill = false # Shows GPU(s) memory #enable_gpu_memory = false +# How much data is stored at once in terms of time. +#retention = "10m" # These are all the components that support custom theming. Note that colour support # will depend on terminal support. diff --git a/src/app.rs b/src/app.rs index 3669bfa9..ee8b3ee6 100644 --- a/src/app.rs +++ b/src/app.rs @@ -72,6 +72,7 @@ pub struct AppConfigFields { pub network_unit_type: DataUnit, pub network_scale_type: AxisScaling, pub network_use_binary_prefix: bool, + pub retention_ms: u64, } /// For filtering out information @@ -2186,16 +2187,16 @@ impl App { { let new_time = cpu_widget_state.current_display_time + self.app_config_fields.time_interval; - if new_time <= constants::STALE_MAX_MILLISECONDS { + if new_time <= self.app_config_fields.retention_ms { cpu_widget_state.current_display_time = new_time; self.cpu_state.force_update = Some(self.current_widget.widget_id); if self.app_config_fields.autohide_time { cpu_widget_state.autohide_timer = Some(Instant::now()); } } else if cpu_widget_state.current_display_time - != constants::STALE_MAX_MILLISECONDS + != self.app_config_fields.retention_ms { - cpu_widget_state.current_display_time = constants::STALE_MAX_MILLISECONDS; + cpu_widget_state.current_display_time = self.app_config_fields.retention_ms; self.cpu_state.force_update = Some(self.current_widget.widget_id); if self.app_config_fields.autohide_time { cpu_widget_state.autohide_timer = Some(Instant::now()); @@ -2211,16 +2212,16 @@ impl App { { let new_time = mem_widget_state.current_display_time + self.app_config_fields.time_interval; - if new_time <= constants::STALE_MAX_MILLISECONDS { + if new_time <= self.app_config_fields.retention_ms { mem_widget_state.current_display_time = new_time; self.mem_state.force_update = Some(self.current_widget.widget_id); if self.app_config_fields.autohide_time { mem_widget_state.autohide_timer = Some(Instant::now()); } } else if mem_widget_state.current_display_time - != constants::STALE_MAX_MILLISECONDS + != self.app_config_fields.retention_ms { - mem_widget_state.current_display_time = constants::STALE_MAX_MILLISECONDS; + mem_widget_state.current_display_time = self.app_config_fields.retention_ms; self.mem_state.force_update = Some(self.current_widget.widget_id); if self.app_config_fields.autohide_time { mem_widget_state.autohide_timer = Some(Instant::now()); @@ -2236,16 +2237,16 @@ impl App { { let new_time = net_widget_state.current_display_time + self.app_config_fields.time_interval; - if new_time <= constants::STALE_MAX_MILLISECONDS { + if new_time <= self.app_config_fields.retention_ms { net_widget_state.current_display_time = new_time; self.net_state.force_update = Some(self.current_widget.widget_id); if self.app_config_fields.autohide_time { net_widget_state.autohide_timer = Some(Instant::now()); } } else if net_widget_state.current_display_time - != constants::STALE_MAX_MILLISECONDS + != self.app_config_fields.retention_ms { - net_widget_state.current_display_time = constants::STALE_MAX_MILLISECONDS; + net_widget_state.current_display_time = self.app_config_fields.retention_ms; self.net_state.force_update = Some(self.current_widget.widget_id); if self.app_config_fields.autohide_time { net_widget_state.autohide_timer = Some(Instant::now()); diff --git a/src/bin/main.rs b/src/bin/main.rs index a1fdcbc1..2130ecb3 100644 --- a/src/bin/main.rs +++ b/src/bin/main.rs @@ -85,12 +85,12 @@ fn main() -> Result<()> { let lock = thread_termination_lock.clone(); let cvar = thread_termination_cvar.clone(); let cleaning_sender = sender.clone(); - const OFFSET_WAIT_TIME: u64 = constants::STALE_MAX_MILLISECONDS + 60000; + let offset_wait_time = app.app_config_fields.retention_ms + 60000; thread::spawn(move || { loop { let result = cvar.wait_timeout( lock.lock().unwrap(), - Duration::from_millis(OFFSET_WAIT_TIME), + Duration::from_millis(offset_wait_time), ); if let Ok(result) = result { if *(result.0) { @@ -271,7 +271,7 @@ fn main() -> Result<()> { } BottomEvent::Clean => { app.data_collection - .clean_data(constants::STALE_MAX_MILLISECONDS); + .clean_data(app.app_config_fields.retention_ms); } } } diff --git a/src/clap.rs b/src/clap.rs index b26c4c6a..043b79b3 100644 --- a/src/clap.rs +++ b/src/clap.rs @@ -348,6 +348,13 @@ use CPU (3) as the default instead. "Displays the network widget with binary prefixes (i.e. kibibits, mebibits) rather than a decimal prefix (i.e. kilobits, megabits). Defaults to decimal prefixes.", ); + let retention = Arg::new("retention") + .long("retention") + .takes_value(true) + .value_name("time") + .help("The timespan of data kept.") + .long_help("How much data is stored at once in terms of time. Takes in human-readable time spans (e.g. 10m, 1h), with a minimum of 1 minute. Note higher values will take up more memory. Defaults to 10 minutes."); + #[allow(unused_mut)] let mut app = Command::new(crate_name!()) .version(crate_version!()) @@ -391,7 +398,8 @@ use CPU (3) as the default instead. .arg(network_use_binary_prefix) .arg(current_usage) .arg(use_old_network_legend) - .arg(whole_word); + .arg(whole_word) + .arg(retention); #[cfg(feature = "battery")] { diff --git a/src/constants.rs b/src/constants.rs index 6c8c9546..4cdb1369 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -5,9 +5,6 @@ use crate::options::ConfigColours; // Default widget ID pub const DEFAULT_WIDGET_ID: u64 = 56709; -// How long to store data. -pub const STALE_MAX_MILLISECONDS: u64 = 600 * 1000; // Keep 10 minutes of data. - // How much data is SHOWN pub const DEFAULT_TIME_MILLISECONDS: u64 = 60 * 1000; // Defaults to 1 min. pub const STALE_MIN_MILLISECONDS: u64 = 30 * 1000; // Lowest is 30 seconds @@ -549,6 +546,8 @@ pub const CONFIG_TEXT: &str = r##"# This is a default config file for bottom. A #disable_advanced_kill = false # Shows GPU(s) memory #enable_gpu_memory = false +# How much data is stored at once in terms of time. +#retention = "10m" # These are all the components that support custom theming. Note that colour support # will depend on terminal support. diff --git a/src/options.rs b/src/options.rs index cc8b57f3..22491468 100644 --- a/src/options.rs +++ b/src/options.rs @@ -3,9 +3,10 @@ use std::{ collections::{HashMap, HashSet}, convert::TryInto, str::FromStr, - time::Instant, + time::{Duration, Instant}, }; +use clap::ArgMatches; use layout_options::*; use regex::Regex; use serde::{Deserialize, Serialize}; @@ -88,6 +89,9 @@ pub struct ConfigFlags { pub network_use_log: Option, pub network_use_binary_prefix: Option, pub enable_gpu_memory: Option, + #[serde(with = "humantime_serde")] + #[serde(default)] + pub retention: Option, } #[derive(Clone, Default, Debug, Deserialize, Serialize)] @@ -168,13 +172,16 @@ pub struct IgnoreList { } pub fn build_app( - matches: &clap::ArgMatches, config: &mut Config, widget_layout: &BottomLayout, + matches: &ArgMatches, config: &mut Config, widget_layout: &BottomLayout, default_widget_id: u64, default_widget_type_option: &Option, colours: &CanvasColours, ) -> Result { use BottomWidgetType::*; + + let retention_ms = + get_retention_ms(matches, config).context("Update `retention` in your config file.")?; let autohide_time = get_autohide_time(matches, config); - let default_time_value = get_default_time_value(matches, config) + 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 = get_use_basic_mode(matches, config); @@ -224,7 +231,7 @@ pub fn build_app( use_current_cpu_total: get_use_current_cpu_total(matches, config), use_basic_mode, default_time_value, - time_interval: get_time_interval(matches, config) + time_interval: get_time_interval(matches, config, retention_ms) .context("Update 'time_delta' in your config file.")?, hide_time: get_hide_time(matches, config), autohide_time, @@ -241,6 +248,7 @@ pub fn build_app( network_scale_type, network_unit_type, network_use_binary_prefix, + retention_ms, }; for row in &widget_layout.rows { @@ -422,7 +430,7 @@ pub fn build_app( } pub fn get_widget_layout( - matches: &clap::ArgMatches, config: &Config, + matches: &ArgMatches, config: &Config, ) -> error::Result<(BottomLayout, u64, Option)> { let left_legend = get_use_left_legend(matches, config); let (default_widget_type, mut default_widget_count) = @@ -486,9 +494,7 @@ pub fn get_widget_layout( Ok((bottom_layout, default_widget_id, default_widget_type)) } -fn get_update_rate_in_milliseconds( - matches: &clap::ArgMatches, config: &Config, -) -> error::Result { +fn get_update_rate_in_milliseconds(matches: &ArgMatches, config: &Config) -> error::Result { let update_rate_in_milliseconds = if let Some(update_rate) = matches.value_of("rate") { update_rate.parse::().map_err(|_| { BottomError::ConfigError( @@ -515,7 +521,7 @@ fn get_update_rate_in_milliseconds( } fn get_temperature( - matches: &clap::ArgMatches, config: &Config, + matches: &ArgMatches, config: &Config, ) -> error::Result { if matches.is_present("fahrenheit") { return Ok(data_harvester::temperature::TemperatureType::Fahrenheit); @@ -541,7 +547,7 @@ fn get_temperature( } /// Yes, this function gets whether to show average CPU (true) or not (false) -fn get_show_average_cpu(matches: &clap::ArgMatches, config: &Config) -> bool { +fn get_show_average_cpu(matches: &ArgMatches, config: &Config) -> bool { if matches.is_present("hide_avg_cpu") { return false; } else if let Some(flags) = &config.flags { @@ -553,7 +559,7 @@ fn get_show_average_cpu(matches: &clap::ArgMatches, config: &Config) -> bool { true } -fn get_use_dot(matches: &clap::ArgMatches, config: &Config) -> bool { +fn get_use_dot(matches: &ArgMatches, config: &Config) -> bool { if matches.is_present("dot_marker") { return true; } else if let Some(flags) = &config.flags { @@ -564,7 +570,7 @@ fn get_use_dot(matches: &clap::ArgMatches, config: &Config) -> bool { false } -fn get_use_left_legend(matches: &clap::ArgMatches, config: &Config) -> bool { +fn get_use_left_legend(matches: &ArgMatches, config: &Config) -> bool { if matches.is_present("left_legend") { return true; } else if let Some(flags) = &config.flags { @@ -576,7 +582,7 @@ fn get_use_left_legend(matches: &clap::ArgMatches, config: &Config) -> bool { false } -fn get_use_current_cpu_total(matches: &clap::ArgMatches, config: &Config) -> bool { +fn get_use_current_cpu_total(matches: &ArgMatches, config: &Config) -> bool { if matches.is_present("current_usage") { return true; } else if let Some(flags) = &config.flags { @@ -588,7 +594,7 @@ fn get_use_current_cpu_total(matches: &clap::ArgMatches, config: &Config) -> boo false } -fn get_use_basic_mode(matches: &clap::ArgMatches, config: &Config) -> bool { +fn get_use_basic_mode(matches: &ArgMatches, config: &Config) -> bool { if matches.is_present("basic") { return true; } else if let Some(flags) = &config.flags { @@ -600,7 +606,10 @@ fn get_use_basic_mode(matches: &clap::ArgMatches, config: &Config) -> bool { false } -fn get_default_time_value(matches: &clap::ArgMatches, config: &Config) -> error::Result { +/// FIXME: Let this accept human times. +fn get_default_time_value( + matches: &ArgMatches, config: &Config, retention_ms: u64, +) -> error::Result { let default_time = if let Some(default_time_value) = matches.value_of("default_time_value") { default_time_value.parse::().map_err(|_| { BottomError::ConfigError( @@ -621,17 +630,19 @@ fn get_default_time_value(matches: &clap::ArgMatches, config: &Config) -> error: return Err(BottomError::ConfigError( "set your default value to be at least 30000 milliseconds.".to_string(), )); - } else if default_time > STALE_MAX_MILLISECONDS { + } else if default_time > retention_ms { return Err(BottomError::ConfigError(format!( "set your default value to be at most {} milliseconds.", - STALE_MAX_MILLISECONDS + retention_ms ))); } Ok(default_time) } -fn get_time_interval(matches: &clap::ArgMatches, config: &Config) -> error::Result { +fn get_time_interval( + matches: &ArgMatches, config: &Config, retention_ms: u64, +) -> error::Result { let time_interval = if let Some(time_interval) = matches.value_of("time_delta") { time_interval.parse::().map_err(|_| { BottomError::ConfigError( @@ -652,17 +663,17 @@ fn get_time_interval(matches: &clap::ArgMatches, config: &Config) -> error::Resu return Err(BottomError::ConfigError( "set your time delta to be at least 1000 milliseconds.".to_string(), )); - } else if time_interval > STALE_MAX_MILLISECONDS { + } else if time_interval > retention_ms { return Err(BottomError::ConfigError(format!( "set your time delta to be at most {} milliseconds.", - STALE_MAX_MILLISECONDS + retention_ms ))); } Ok(time_interval) } -pub fn get_app_grouping(matches: &clap::ArgMatches, config: &Config) -> bool { +pub fn get_app_grouping(matches: &ArgMatches, config: &Config) -> bool { if matches.is_present("group") { return true; } else if let Some(flags) = &config.flags { @@ -673,7 +684,7 @@ pub fn get_app_grouping(matches: &clap::ArgMatches, config: &Config) -> bool { false } -pub fn get_app_case_sensitive(matches: &clap::ArgMatches, config: &Config) -> bool { +pub fn get_app_case_sensitive(matches: &ArgMatches, config: &Config) -> bool { if matches.is_present("case_sensitive") { return true; } else if let Some(flags) = &config.flags { @@ -684,7 +695,7 @@ pub fn get_app_case_sensitive(matches: &clap::ArgMatches, config: &Config) -> bo false } -pub fn get_app_match_whole_word(matches: &clap::ArgMatches, config: &Config) -> bool { +pub fn get_app_match_whole_word(matches: &ArgMatches, config: &Config) -> bool { if matches.is_present("whole_word") { return true; } else if let Some(flags) = &config.flags { @@ -695,7 +706,7 @@ pub fn get_app_match_whole_word(matches: &clap::ArgMatches, config: &Config) -> false } -pub fn get_app_use_regex(matches: &clap::ArgMatches, config: &Config) -> bool { +pub fn get_app_use_regex(matches: &ArgMatches, config: &Config) -> bool { if matches.is_present("regex") { return true; } else if let Some(flags) = &config.flags { @@ -706,7 +717,7 @@ pub fn get_app_use_regex(matches: &clap::ArgMatches, config: &Config) -> bool { false } -fn get_hide_time(matches: &clap::ArgMatches, config: &Config) -> bool { +fn get_hide_time(matches: &ArgMatches, config: &Config) -> bool { if matches.is_present("hide_time") { return true; } else if let Some(flags) = &config.flags { @@ -717,7 +728,7 @@ fn get_hide_time(matches: &clap::ArgMatches, config: &Config) -> bool { false } -fn get_autohide_time(matches: &clap::ArgMatches, config: &Config) -> bool { +fn get_autohide_time(matches: &ArgMatches, config: &Config) -> bool { if matches.is_present("autohide_time") { return true; } else if let Some(flags) = &config.flags { @@ -730,7 +741,7 @@ fn get_autohide_time(matches: &clap::ArgMatches, config: &Config) -> bool { } fn get_default_widget_and_count( - matches: &clap::ArgMatches, config: &Config, + matches: &ArgMatches, config: &Config, ) -> error::Result<(Option, u64)> { let widget_type = if let Some(widget_type) = matches.value_of("default_widget_type") { let parsed_widget = widget_type.parse::()?; @@ -779,7 +790,7 @@ fn get_default_widget_and_count( } } -fn get_disable_click(matches: &clap::ArgMatches, config: &Config) -> bool { +fn get_disable_click(matches: &ArgMatches, config: &Config) -> bool { if matches.is_present("disable_click") { return true; } else if let Some(flags) = &config.flags { @@ -790,7 +801,7 @@ fn get_disable_click(matches: &clap::ArgMatches, config: &Config) -> bool { false } -fn get_use_old_network_legend(matches: &clap::ArgMatches, config: &Config) -> bool { +fn get_use_old_network_legend(matches: &ArgMatches, config: &Config) -> bool { if matches.is_present("use_old_network_legend") { return true; } else if let Some(flags) = &config.flags { @@ -801,7 +812,7 @@ fn get_use_old_network_legend(matches: &clap::ArgMatches, config: &Config) -> bo false } -fn get_hide_table_gap(matches: &clap::ArgMatches, config: &Config) -> bool { +fn get_hide_table_gap(matches: &ArgMatches, config: &Config) -> bool { if matches.is_present("hide_table_gap") { return true; } else if let Some(flags) = &config.flags { @@ -812,7 +823,7 @@ fn get_hide_table_gap(matches: &clap::ArgMatches, config: &Config) -> bool { false } -fn get_use_battery(matches: &clap::ArgMatches, config: &Config) -> bool { +fn get_use_battery(matches: &ArgMatches, config: &Config) -> bool { if cfg!(feature = "battery") { if matches.is_present("battery") { return true; @@ -825,7 +836,7 @@ fn get_use_battery(matches: &clap::ArgMatches, config: &Config) -> bool { false } -fn get_enable_gpu_memory(matches: &clap::ArgMatches, config: &Config) -> bool { +fn get_enable_gpu_memory(matches: &ArgMatches, config: &Config) -> bool { if cfg!(feature = "gpu") { if matches.is_present("enable_gpu_memory") { return true; @@ -839,7 +850,7 @@ fn get_enable_gpu_memory(matches: &clap::ArgMatches, config: &Config) -> bool { } #[allow(dead_code)] -fn get_no_write(matches: &clap::ArgMatches, config: &Config) -> bool { +fn get_no_write(matches: &ArgMatches, config: &Config) -> bool { if matches.is_present("no_write") { return true; } else if let Some(flags) = &config.flags { @@ -887,9 +898,7 @@ fn get_ignore_list(ignore_list: &Option) -> error::Result error::Result { +pub fn get_color_scheme(matches: &ArgMatches, config: &Config) -> error::Result { if let Some(color) = matches.value_of("color") { // Highest priority is always command line flags... return ColourScheme::from_str(color); @@ -914,7 +923,7 @@ pub fn get_color_scheme( Ok(ColourScheme::Default) } -fn get_mem_as_value(matches: &clap::ArgMatches, config: &Config) -> bool { +fn get_mem_as_value(matches: &ArgMatches, config: &Config) -> bool { if matches.is_present("mem_as_value") { return true; } else if let Some(flags) = &config.flags { @@ -925,7 +934,7 @@ fn get_mem_as_value(matches: &clap::ArgMatches, config: &Config) -> bool { false } -fn get_is_default_tree(matches: &clap::ArgMatches, config: &Config) -> bool { +fn get_is_default_tree(matches: &ArgMatches, config: &Config) -> bool { if matches.is_present("tree") { return true; } else if let Some(flags) = &config.flags { @@ -936,7 +945,7 @@ fn get_is_default_tree(matches: &clap::ArgMatches, config: &Config) -> bool { false } -fn get_show_table_scroll_position(matches: &clap::ArgMatches, config: &Config) -> bool { +fn get_show_table_scroll_position(matches: &ArgMatches, config: &Config) -> bool { if matches.is_present("show_table_scroll_position") { return true; } else if let Some(flags) = &config.flags { @@ -947,7 +956,7 @@ fn get_show_table_scroll_position(matches: &clap::ArgMatches, config: &Config) - false } -fn get_is_default_process_command(matches: &clap::ArgMatches, config: &Config) -> bool { +fn get_is_default_process_command(matches: &ArgMatches, config: &Config) -> bool { if matches.is_present("process_command") { return true; } else if let Some(flags) = &config.flags { @@ -958,7 +967,7 @@ fn get_is_default_process_command(matches: &clap::ArgMatches, config: &Config) - false } -fn get_is_advanced_kill_disabled(matches: &clap::ArgMatches, config: &Config) -> bool { +fn get_is_advanced_kill_disabled(matches: &ArgMatches, config: &Config) -> bool { if matches.is_present("disable_advanced_kill") { return true; } else if let Some(flags) = &config.flags { @@ -969,7 +978,7 @@ fn get_is_advanced_kill_disabled(matches: &clap::ArgMatches, config: &Config) -> false } -fn get_network_unit_type(matches: &clap::ArgMatches, config: &Config) -> DataUnit { +fn get_network_unit_type(matches: &ArgMatches, config: &Config) -> DataUnit { if matches.is_present("network_use_bytes") { return DataUnit::Byte; } else if let Some(flags) = &config.flags { @@ -983,7 +992,7 @@ fn get_network_unit_type(matches: &clap::ArgMatches, config: &Config) -> DataUni DataUnit::Bit } -fn get_network_scale_type(matches: &clap::ArgMatches, config: &Config) -> AxisScaling { +fn get_network_scale_type(matches: &ArgMatches, config: &Config) -> AxisScaling { if matches.is_present("network_use_log") { return AxisScaling::Log; } else if let Some(flags) = &config.flags { @@ -997,7 +1006,7 @@ fn get_network_scale_type(matches: &clap::ArgMatches, config: &Config) -> AxisSc AxisScaling::Linear } -fn get_network_use_binary_prefix(matches: &clap::ArgMatches, config: &Config) -> bool { +fn get_network_use_binary_prefix(matches: &ArgMatches, config: &Config) -> bool { if matches.is_present("network_use_binary_prefix") { return true; } else if let Some(flags) = &config.flags { @@ -1007,3 +1016,21 @@ fn get_network_use_binary_prefix(matches: &clap::ArgMatches, config: &Config) -> } false } + +fn get_retention_ms(matches: &ArgMatches, config: &Config) -> error::Result { + const DEFAULT_RETENTION_MS: u64 = 600 * 1000; // Keep 10 minutes of data. + + if let Some(retention) = matches.value_of("retention") { + humantime::parse_duration(retention) + .map(|dur| dur.as_millis() as u64) + .map_err(|err| BottomError::ConfigError(format!("invalid retention duration: {err:?}"))) + } else if let Some(flags) = &config.flags { + if let Some(retention) = flags.retention { + Ok(retention.as_millis() as u64) + } else { + Ok(DEFAULT_RETENTION_MS) + } + } else { + Ok(DEFAULT_RETENTION_MS) + } +}