summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorClement Tsang <34804052+ClementTsang@users.noreply.github.com>2023-05-26 00:42:40 -0400
committerGitHub <noreply@github.com>2023-05-26 00:42:40 -0400
commiteccaf11937186c1ddf3710cbcee0b2c10e8103f3 (patch)
tree5c67d5f08c285d63671f8adb653539a44eec50dc
parent6b9663a2c20b6483c1f4b1b9cc964719cfe43a0e (diff)
feature: support human times for `default_time_value` and `time_delta` (#1172)
* feature: support human times for time interval and default range * add tests, fix not using ms * appease clippy * changelog
-rw-r--r--CHANGELOG.md6
-rw-r--r--Cargo.lock2
-rw-r--r--Cargo.toml2
-rw-r--r--src/options.rs168
4 files changed, 157 insertions, 21 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 7718cde3..cfc75a10 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+## [0.9.2] - Unreleased
+
+## Features
+
+- [#1172](https://github.com/ClementTsang/bottom/pull/1172): Support human times for `time_delta` and `default_time_value`.
+
## [0.9.1] - 2023-05-14
## Bug Fixes
diff --git a/Cargo.lock b/Cargo.lock
index a0c3d88d..1f035890 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -136,7 +136,7 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "bottom"
-version = "0.9.1"
+version = "0.9.2"
dependencies = [
"anyhow",
"assert_cmd",
diff --git a/Cargo.toml b/Cargo.toml
index 71141942..f637c690 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "bottom"
-version = "0.9.1"
+version = "0.9.2"
authors = ["Clement Tsang <cjhtsang@uwaterloo.ca>"]
edition = "2021"
repository = "https://github.com/ClementTsang/bottom"
diff --git a/src/options.rs b/src/options.rs
index 5644ddcc..367d6233 100644
--- a/src/options.rs
+++ b/src/options.rs
@@ -57,8 +57,8 @@ pub struct ConfigFlags {
pub whole_word: Option<bool>,
pub regex: Option<bool>,
pub basic: Option<bool>,
- pub default_time_value: Option<u64>,
- pub time_delta: Option<u64>,
+ pub default_time_value: Option<String>,
+ pub time_delta: Option<String>,
pub autohide_time: Option<bool>,
pub hide_time: Option<bool>,
pub default_widget_type: Option<String>,
@@ -582,20 +582,27 @@ fn get_show_average_cpu(matches: &ArgMatches, config: &Config) -> bool {
true
}
-/// FIXME: Let this accept human times.
+fn try_parse_ms(s: &str) -> error::Result<u64> {
+ if let Ok(val) = humantime::parse_duration(s) {
+ Ok(val.as_millis().try_into()?)
+ } else if let Ok(val) = s.parse::<u64>() {
+ Ok(val)
+ } else {
+ Err(BottomError::ConfigError(
+ "could not parse as a valid 64-bit unsigned integer or a human time".to_string(),
+ ))
+ }
+}
+
fn get_default_time_value(
matches: &ArgMatches, config: &Config, retention_ms: u64,
) -> error::Result<u64> {
let default_time =
if let Some(default_time_value) = matches.get_one::<String>("default_time_value") {
- default_time_value.parse::<u64>().map_err(|_| {
- BottomError::ConfigError(
- "could not parse as a valid 64-bit unsigned integer".to_string(),
- )
- })?
+ try_parse_ms(default_time_value)?
} else if let Some(flags) = &config.flags {
- if let Some(default_time_value) = flags.default_time_value {
- default_time_value
+ if let Some(default_time_value) = &flags.default_time_value {
+ try_parse_ms(default_time_value)?
} else {
DEFAULT_TIME_MILLISECONDS
}
@@ -621,14 +628,10 @@ fn get_time_interval(
matches: &ArgMatches, config: &Config, retention_ms: u64,
) -> error::Result<u64> {
let time_interval = if let Some(time_interval) = matches.get_one::<String>("time_delta") {
- time_interval.parse::<u64>().map_err(|_| {
- BottomError::ConfigError(
- "could not parse as a valid 64-bit unsigned integer".to_string(),
- )
- })?
+ try_parse_ms(time_interval)?
} else if let Some(flags) = &config.flags {
- if let Some(time_interval) = flags.time_delta {
- time_interval
+ if let Some(time_interval) = &flags.time_delta {
+ try_parse_ms(time_interval)?
} else {
TIME_CHANGE_MILLISECONDS
}
@@ -869,8 +872,135 @@ fn get_retention_ms(matches: &ArgMatches, config: &Config) -> error::Result<u64>
mod test {
use clap::ArgMatches;
- use super::{get_color_scheme, get_widget_layout, Config};
- use crate::{app::App, canvas::canvas_styling::CanvasStyling};
+ use super::{get_color_scheme, get_time_interval, get_widget_layout, Config};
+ use crate::{
+ app::App,
+ canvas::canvas_styling::CanvasStyling,
+ options::{get_default_time_value, try_parse_ms, ConfigFlags},
+ };
+
+ #[test]
+ fn verify_try_parse_ms() {
+ let a = "100s";
+ let b = "100";
+ let c = "1 min";
+ let d = "1 hour 1 min";
+
+ assert_eq!(try_parse_ms(a), Ok(100 * 1000));
+ assert_eq!(try_parse_ms(b), Ok(100));
+ assert_eq!(try_parse_ms(c), Ok(60 * 1000));
+ assert_eq!(try_parse_ms(d), Ok(3660 * 1000));
+
+ let a_bad = "1 test";
+ let b_bad = "-100";
+
+ assert!(try_parse_ms(a_bad).is_err());
+ assert!(try_parse_ms(b_bad).is_err());
+ }
+
+ #[test]
+ fn matches_human_times() {
+ let config = Config::default();
+ let app = crate::clap::build_app();
+
+ {
+ let app = app.clone();
+ let delta_args = vec!["btm", "--time_delta", "2 min"];
+ let matches = app.get_matches_from(delta_args);
+
+ assert_eq!(
+ get_time_interval(&matches, &config, 60 * 60 * 1000),
+ Ok(2 * 60 * 1000)
+ );
+ }
+
+ {
+ let default_time_args = vec!["btm", "--default_time_value", "300s"];
+ let matches = app.get_matches_from(default_time_args);
+
+ assert_eq!(
+ get_default_time_value(&matches, &config, 60 * 60 * 1000),
+ Ok(5 * 60 * 1000)
+ );
+ }
+ }
+
+ #[test]
+ fn matches_number_times() {
+ let config = Config::default();
+ let app = crate::clap::build_app();
+
+ {
+ let app = app.clone();
+ let delta_args = vec!["btm", "--time_delta", "120000"];
+ let matches = app.get_matches_from(delta_args);
+
+ assert_eq!(
+ get_time_interval(&matches, &config, 60 * 60 * 1000),
+ Ok(2 * 60 * 1000)
+ );
+ }
+
+ {
+ let default_time_args = vec!["btm", "--default_time_value", "300000"];
+ let matches = app.get_matches_from(default_time_args);
+
+ assert_eq!(
+ get_default_time_value(&matches, &config, 60 * 60 * 1000),
+ Ok(5 * 60 * 1000)
+ );
+ }
+ }
+
+ #[test]
+ fn config_human_times() {
+ let app = crate::clap::build_app();
+ let matches = app.get_matches_from(["btm"]);
+
+ let mut config = Config::default();
+ let flags = ConfigFlags {
+ time_delta: Some("2 min".to_string()),
+ default_time_value: Some("300s".to_string()),
+ ..Default::default()
+ };
+
+ config.flags = Some(flags);
+
+ assert_eq!(
+ get_time_interval(&matches, &config, 60 * 60 * 1000),
+ Ok(2 * 60 * 1000)
+ );
+
+ assert_eq!(
+ get_default_time_value(&matches, &config, 60 * 60 * 1000),
+ Ok(5 * 60 * 1000)
+ );
+ }
+
+ #[test]
+ fn config_number_times() {
+ let app = crate::clap::build_app();
+ let matches = app.get_matches_from(["btm"]);
+
+ let mut config = Config::default();
+ let flags = ConfigFlags {
+ time_delta: Some("120000".to_string()),
+ default_time_value: Some("300000".to_string()),
+ ..Default::default()
+ };
+
+ config.flags = Some(flags);
+
+ assert_eq!(
+ get_time_interval(&matches, &config, 60 * 60 * 1000),
+ Ok(2 * 60 * 1000)
+ );
+
+ assert_eq!(
+ get_default_time_value(&matches, &config, 60 * 60 * 1000),
+ Ok(5 * 60 * 1000)
+ );
+ }
fn create_app(mut config: Config, matches: ArgMatches) -> App {
let (layout, id, ty) = get_widget_layout(&matches, &config).unwrap();