From f91713b714378d6f18e75722db89e50e9eb99ff6 Mon Sep 17 00:00:00 2001 From: Stefan Holderbach Date: Sun, 30 Jul 2023 22:23:36 +0200 Subject: [PATCH] Add `format duration` to replace `into duration --convert` (#9788) # Description Add `format duration` cmd to choose output unit. This takes the previous `into duration --convert ...` behavior which returned a string into its own `format duration` command. This was suprising and not fitting with the general type signature for the `into ...` commands. This command for now lives in the `nu-cmd-extra` nursery. # User-Facing Changes ## Breaking change Removes formatting behavior from `into duration` Now use `format duration` instead of `into duration --convert` ## Usage: ``` 1sec | format duration us # Output data in microseconds "2ms" | into duration | format duration sec # go from string to string ``` # Tests + Formatting Basic example testing (including basic broadcast) --- crates/nu-cmd-extra/src/extra/mod.rs | 1 + .../src/extra/strings/format/duration.rs | 197 ++++++++ .../src/extra/strings/format/mod.rs | 2 + .../src/conversions/into/duration.rs | 430 +----------------- 4 files changed, 222 insertions(+), 408 deletions(-) create mode 100644 crates/nu-cmd-extra/src/extra/strings/format/duration.rs diff --git a/crates/nu-cmd-extra/src/extra/mod.rs b/crates/nu-cmd-extra/src/extra/mod.rs index 009eb16805..fc69f76b45 100644 --- a/crates/nu-cmd-extra/src/extra/mod.rs +++ b/crates/nu-cmd-extra/src/extra/mod.rs @@ -86,6 +86,7 @@ pub fn add_extra_command_context(mut engine_state: EngineState) -> EngineState { bind_command!( strings::format::Format, strings::format::FileSize, + strings::format::FormatDuration, strings::encode_decode::EncodeHex, strings::encode_decode::DecodeHex ); diff --git a/crates/nu-cmd-extra/src/extra/strings/format/duration.rs b/crates/nu-cmd-extra/src/extra/strings/format/duration.rs new file mode 100644 index 0000000000..38d1eccef3 --- /dev/null +++ b/crates/nu-cmd-extra/src/extra/strings/format/duration.rs @@ -0,0 +1,197 @@ +use nu_cmd_base::input_handler::{operate, CmdArgument}; +use nu_engine::CallExt; +use nu_protocol::ast::{Call, CellPath}; +use nu_protocol::engine::{Command, EngineState, Stack}; +use nu_protocol::{ + Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value, +}; + +struct Arguments { + format_value: String, + float_precision: usize, + cell_paths: Option>, +} + +impl CmdArgument for Arguments { + fn take_cell_paths(&mut self) -> Option> { + self.cell_paths.take() + } +} + +#[derive(Clone)] +pub struct FormatDuration; + +impl Command for FormatDuration { + fn name(&self) -> &str { + "format duration" + } + + fn signature(&self) -> Signature { + Signature::build("format duration") + .input_output_types(vec![ + (Type::Duration, Type::String), + ( + Type::List(Box::new(Type::Duration)), + Type::List(Box::new(Type::String)), + ), + (Type::Table(vec![]), Type::Table(vec![])), + ]) + .allow_variants_without_examples(true) + .required( + "format value", + SyntaxShape::String, + "the unit in which to display the duration", + ) + .rest( + "rest", + SyntaxShape::CellPath, + "For a data structure input, format duration at the given cell paths", + ) + .category(Category::Strings) + } + + fn usage(&self) -> &str { + "Outputs duration with a specified unit of time." + } + + fn search_terms(&self) -> Vec<&str> { + vec!["convert", "display", "pattern", "human readable"] + } + + fn run( + &self, + engine_state: &EngineState, + stack: &mut Stack, + call: &Call, + input: PipelineData, + ) -> Result { + let format_value = call + .req::(engine_state, stack, 0)? + .as_string()? + .to_ascii_lowercase(); + let cell_paths: Vec = call.rest(engine_state, stack, 1)?; + let cell_paths = (!cell_paths.is_empty()).then_some(cell_paths); + let float_precision = engine_state.config.float_precision as usize; + let arg = Arguments { + format_value, + float_precision, + cell_paths, + }; + operate( + format_value_impl, + arg, + input, + call.head, + engine_state.ctrlc.clone(), + ) + } + + fn examples(&self) -> Vec { + vec![ + Example { + description: "Convert µs duration to the requested second duration as a string", + example: "1000000µs | format duration sec", + result: Some(Value::test_string("1 sec")), + }, + Example { + description: "Convert durations to µs duration as strings", + example: "[1sec 2sec] | format duration µs", + result: Some(Value::test_list(vec![ + Value::test_string("1000000 µs"), + Value::test_string("2000000 µs"), + ])), + }, + Example { + description: "Convert duration to µs as a string if unit asked for was us", + example: "1sec | format duration us", + result: Some(Value::test_string("1000000 µs")), + }, + ] + } +} + +fn format_value_impl(val: &Value, arg: &Arguments, span: Span) -> Value { + match val { + Value::Duration { + val: inner, + span: inner_span, + } => { + let duration = *inner; + let float_precision = arg.float_precision; + match convert_inner_to_unit(duration, &arg.format_value, span, *inner_span) { + Ok(d) => { + let unit = if &arg.format_value == "us" { + "µs" + } else { + &arg.format_value + }; + if d.fract() == 0.0 { + Value::String { + val: format!("{} {}", d, unit), + span: *inner_span, + } + } else { + Value::String { + val: format!("{:.float_precision$} {}", d, unit), + span: *inner_span, + } + } + } + Err(e) => Value::Error { error: Box::new(e) }, + } + } + Value::Error { .. } => val.clone(), + _ => Value::Error { + error: Box::new(ShellError::OnlySupportsThisInputType { + exp_input_type: "filesize".into(), + wrong_type: val.get_type().to_string(), + dst_span: span, + src_span: val.expect_span(), + }), + }, + } +} + +fn convert_inner_to_unit( + val: i64, + to_unit: &str, + span: Span, + value_span: Span, +) -> Result { + match to_unit { + "ns" => Ok(val as f64), + "us" => Ok(val as f64 / 1000.0), + "µs" => Ok(val as f64 / 1000.0), // Micro sign + "μs" => Ok(val as f64 / 1000.0), // Greek small letter + "ms" => Ok(val as f64 / 1000.0 / 1000.0), + "sec" => Ok(val as f64 / 1000.0 / 1000.0 / 1000.0), + "min" => Ok(val as f64 / 1000.0 / 1000.0 / 1000.0 / 60.0), + "hr" => Ok(val as f64 / 1000.0 / 1000.0 / 1000.0 / 60.0 / 60.0), + "day" => Ok(val as f64 / 1000.0 / 1000.0 / 1000.0 / 60.0 / 60.0 / 24.0), + "wk" => Ok(val as f64 / 1000.0 / 1000.0 / 1000.0 / 60.0 / 60.0 / 24.0 / 7.0), + "month" => Ok(val as f64 / 1000.0 / 1000.0 / 1000.0 / 60.0 / 60.0 / 24.0 / 30.0), + "yr" => Ok(val as f64 / 1000.0 / 1000.0 / 1000.0 / 60.0 / 60.0 / 24.0 / 365.0), + "dec" => Ok(val as f64 / 10.0 / 1000.0 / 1000.0 / 1000.0 / 60.0 / 60.0 / 24.0 / 365.0), + + _ => Err(ShellError::CantConvertToDuration { + details: to_unit.to_string(), + dst_span: span, + src_span: value_span, + help: Some( + "supported units are ns, us/µs, ms, sec, min, hr, day, wk, month, yr, and dec" + .to_string(), + ), + }), + } +} +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_examples() { + use crate::test_examples; + + test_examples(FormatDuration) + } +} diff --git a/crates/nu-cmd-extra/src/extra/strings/format/mod.rs b/crates/nu-cmd-extra/src/extra/strings/format/mod.rs index c5d20986b2..582ad120cd 100644 --- a/crates/nu-cmd-extra/src/extra/strings/format/mod.rs +++ b/crates/nu-cmd-extra/src/extra/strings/format/mod.rs @@ -1,5 +1,7 @@ mod command; +mod duration; mod filesize; pub(crate) use command::Format; +pub(crate) use duration::FormatDuration; pub(crate) use filesize::FileSize; diff --git a/crates/nu-command/src/conversions/into/duration.rs b/crates/nu-command/src/conversions/into/duration.rs index 66984ac271..8c98ea6a54 100644 --- a/crates/nu-command/src/conversions/into/duration.rs +++ b/crates/nu-command/src/conversions/into/duration.rs @@ -3,8 +3,7 @@ use nu_parser::{parse_unit_value, DURATION_UNIT_GROUPS}; use nu_protocol::{ ast::{Call, CellPath, Expr}, engine::{Command, EngineState, Stack}, - Category, Example, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, Type, Unit, - Value, + Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Unit, Value, }; #[derive(Clone)] @@ -20,19 +19,10 @@ impl Command for SubCommand { .input_output_types(vec![ (Type::String, Type::Duration), (Type::Duration, Type::Duration), - // TODO: --convert option should be implemented as `format duration` - (Type::String, Type::String), - (Type::Duration, Type::String), (Type::Table(vec![]), Type::Table(vec![])), (Type::Record(vec![]), Type::Record(vec![])), ]) .allow_variants_without_examples(true) - .named( - "convert", - SyntaxShape::String, - "convert duration into another duration", - Some('c'), - ) .rest( "rest", SyntaxShape::CellPath, @@ -124,14 +114,6 @@ impl Command for SubCommand { span, }), }, - Example { - description: "Convert string to the requested duration as a string", - example: "'7min' | into duration --convert sec", - result: Some(Value::String { - val: "420 sec".to_string(), - span, - }), - }, Example { description: "Convert duration to duration", example: "420sec | into duration", @@ -140,38 +122,6 @@ impl Command for SubCommand { span, }), }, - Example { - description: "Convert duration to the requested duration as a string", - example: "420sec | into duration --convert ms", - result: Some(Value::String { - val: "420000 ms".to_string(), - span, - }), - }, - Example { - description: "Convert µs duration to the requested duration as a string", - example: "1000000µs | into duration --convert sec", - result: Some(Value::String { - val: "1 sec".to_string(), - span, - }), - }, - Example { - description: "Convert duration to the µs duration as a string", - example: "1sec | into duration --convert µs", - result: Some(Value::String { - val: "1000000 µs".to_string(), - span, - }), - }, - Example { - description: "Convert duration to µs as a string if unit asked for was us", - example: "1sec | into duration --convert us", - result: Some(Value::String { - val: "1000000 µs".to_string(), - span, - }), - }, ] } } @@ -186,23 +136,17 @@ fn into_duration( Some(t) => t, None => call.head, }; - let convert_to_unit: Option> = call.get_flag(engine_state, stack, "convert")?; let column_paths: Vec = call.rest(engine_state, stack, 0)?; - let config = engine_state.get_config(); - let float_precision = config.float_precision as usize; input.map( move |v| { if column_paths.is_empty() { - action(&v, &convert_to_unit, float_precision, span) + action(&v, span) } else { let mut ret = v; for path in &column_paths { - let d = convert_to_unit.clone(); - let r = ret.update_cell_path( - &path.members, - Box::new(move |old| action(old, &d, float_precision, span)), - ); + let r = + ret.update_cell_path(&path.members, Box::new(move |old| action(old, span))); if let Err(error) = r { return Value::Error { error: Box::new(error), @@ -217,198 +161,6 @@ fn into_duration( ) } -fn convert_str_from_unit_to_unit( - val: i64, - from_unit: &str, - to_unit: &str, - span: Span, - value_span: Span, -) -> Result { - match (from_unit, to_unit) { - ("ns", "ns") => Ok(val as f64), - ("ns", "us") => Ok(val as f64 / 1000.0), - ("ns", "µs") => Ok(val as f64 / 1000.0), // Micro sign - ("ns", "μs") => Ok(val as f64 / 1000.0), // Greek small letter - ("ns", "ms") => Ok(val as f64 / 1000.0 / 1000.0), - ("ns", "sec") => Ok(val as f64 / 1000.0 / 1000.0 / 1000.0), - ("ns", "min") => Ok(val as f64 / 1000.0 / 1000.0 / 1000.0 / 60.0), - ("ns", "hr") => Ok(val as f64 / 1000.0 / 1000.0 / 1000.0 / 60.0 / 60.0), - ("ns", "day") => Ok(val as f64 / 1000.0 / 1000.0 / 1000.0 / 60.0 / 60.0 / 24.0), - ("ns", "wk") => Ok(val as f64 / 1000.0 / 1000.0 / 1000.0 / 60.0 / 60.0 / 24.0 / 7.0), - ("ns", "month") => Ok(val as f64 / 1000.0 / 1000.0 / 1000.0 / 60.0 / 60.0 / 24.0 / 30.0), - ("ns", "yr") => Ok(val as f64 / 1000.0 / 1000.0 / 1000.0 / 60.0 / 60.0 / 24.0 / 365.0), - ("ns", "dec") => { - Ok(val as f64 / 10.0 / 1000.0 / 1000.0 / 1000.0 / 60.0 / 60.0 / 24.0 / 365.0) - } - - ("us", "ns") => Ok(val as f64 * 1000.0), - ("us", "us") => Ok(val as f64), - ("us", "µs") => Ok(val as f64), // Micro sign - ("us", "μs") => Ok(val as f64), // Greek small letter - ("us", "ms") => Ok(val as f64 / 1000.0), - ("us", "sec") => Ok(val as f64 / 1000.0 / 1000.0), - ("us", "min") => Ok(val as f64 / 1000.0 / 1000.0 / 60.0), - ("us", "hr") => Ok(val as f64 / 1000.0 / 1000.0 / 60.0 / 60.0), - ("us", "day") => Ok(val as f64 / 1000.0 / 1000.0 / 60.0 / 60.0 / 24.0), - ("us", "wk") => Ok(val as f64 / 1000.0 / 1000.0 / 60.0 / 60.0 / 24.0 / 7.0), - ("us", "month") => Ok(val as f64 / 1000.0 / 1000.0 / 60.0 / 60.0 / 24.0 / 30.0), - ("us", "yr") => Ok(val as f64 / 1000.0 / 1000.0 / 60.0 / 60.0 / 24.0 / 365.0), - ("us", "dec") => Ok(val as f64 / 10.0 / 1000.0 / 1000.0 / 60.0 / 60.0 / 24.0 / 365.0), - - // Micro sign - ("µs", "ns") => Ok(val as f64 * 1000.0), - ("µs", "us") => Ok(val as f64), - ("µs", "µs") => Ok(val as f64), // Micro sign - ("µs", "μs") => Ok(val as f64), // Greek small letter - ("µs", "ms") => Ok(val as f64 / 1000.0), - ("µs", "sec") => Ok(val as f64 / 1000.0 / 1000.0), - ("µs", "min") => Ok(val as f64 / 1000.0 / 1000.0 / 60.0), - ("µs", "hr") => Ok(val as f64 / 1000.0 / 1000.0 / 60.0 / 60.0), - ("µs", "day") => Ok(val as f64 / 1000.0 / 1000.0 / 60.0 / 60.0 / 24.0), - ("µs", "wk") => Ok(val as f64 / 1000.0 / 1000.0 / 60.0 / 60.0 / 24.0 / 7.0), - ("µs", "month") => Ok(val as f64 / 1000.0 / 1000.0 / 60.0 / 60.0 / 24.0 / 30.0), - ("µs", "yr") => Ok(val as f64 / 1000.0 / 1000.0 / 60.0 / 60.0 / 24.0 / 365.0), - ("µs", "dec") => Ok(val as f64 / 10.0 / 1000.0 / 1000.0 / 60.0 / 60.0 / 24.0 / 365.0), - - // Greek small letter - ("μs", "ns") => Ok(val as f64 * 1000.0), - ("μs", "us") => Ok(val as f64), - ("μs", "µs") => Ok(val as f64), // Micro sign - ("μs", "μs") => Ok(val as f64), // Greek small letter - ("μs", "ms") => Ok(val as f64 / 1000.0), - ("μs", "sec") => Ok(val as f64 / 1000.0 / 1000.0), - ("μs", "min") => Ok(val as f64 / 1000.0 / 1000.0 / 60.0), - ("μs", "hr") => Ok(val as f64 / 1000.0 / 1000.0 / 60.0 / 60.0), - ("μs", "day") => Ok(val as f64 / 1000.0 / 1000.0 / 60.0 / 60.0 / 24.0), - ("μs", "wk") => Ok(val as f64 / 1000.0 / 1000.0 / 60.0 / 60.0 / 24.0 / 7.0), - ("μs", "month") => Ok(val as f64 / 1000.0 / 1000.0 / 60.0 / 60.0 / 24.0 / 30.0), - ("μs", "yr") => Ok(val as f64 / 1000.0 / 1000.0 / 60.0 / 60.0 / 24.0 / 365.0), - ("μs", "dec") => Ok(val as f64 / 10.0 / 1000.0 / 1000.0 / 60.0 / 60.0 / 24.0 / 365.0), - - ("ms", "ns") => Ok(val as f64 * 1000.0 * 1000.0), - ("ms", "us") => Ok(val as f64 * 1000.0), - ("ms", "µs") => Ok(val as f64 * 1000.0), // Micro sign - ("ms", "μs") => Ok(val as f64 * 1000.0), // Greek small letter - ("ms", "ms") => Ok(val as f64), - ("ms", "sec") => Ok(val as f64 / 1000.0), - ("ms", "min") => Ok(val as f64 / 1000.0 / 60.0), - ("ms", "hr") => Ok(val as f64 / 1000.0 / 60.0 / 60.0), - ("ms", "day") => Ok(val as f64 / 1000.0 / 60.0 / 60.0 / 24.0), - ("ms", "wk") => Ok(val as f64 / 1000.0 / 60.0 / 60.0 / 24.0 / 7.0), - ("ms", "month") => Ok(val as f64 / 1000.0 / 60.0 / 60.0 / 24.0 / 30.0), - ("ms", "yr") => Ok(val as f64 / 1000.0 / 60.0 / 60.0 / 24.0 / 365.0), - ("ms", "dec") => Ok(val as f64 / 10.0 / 1000.0 / 60.0 / 60.0 / 24.0 / 365.0), - - ("sec", "ns") => Ok(val as f64 * 1000.0 * 1000.0 * 1000.0), - ("sec", "us") => Ok(val as f64 * 1000.0 * 1000.0), - ("sec", "µs") => Ok(val as f64 * 1000.0 * 1000.0), // Micro sign - ("sec", "μs") => Ok(val as f64 * 1000.0 * 1000.0), // Greek small letter - ("sec", "ms") => Ok(val as f64 * 1000.0), - ("sec", "sec") => Ok(val as f64), - ("sec", "min") => Ok(val as f64 / 60.0), - ("sec", "hr") => Ok(val as f64 / 60.0 / 60.0), - ("sec", "day") => Ok(val as f64 / 60.0 / 60.0 / 24.0), - ("sec", "wk") => Ok(val as f64 / 60.0 / 60.0 / 24.0 / 7.0), - ("sec", "month") => Ok(val as f64 / 60.0 / 60.0 / 24.0 / 30.0), - ("sec", "yr") => Ok(val as f64 / 60.0 / 60.0 / 24.0 / 365.0), - ("sec", "dec") => Ok(val as f64 / 10.0 / 60.0 / 60.0 / 24.0 / 365.0), - - ("min", "ns") => Ok(val as f64 * 1000.0 * 1000.0 * 1000.0 * 60.0), - ("min", "us") => Ok(val as f64 * 1000.0 * 1000.0 * 60.0), - ("min", "µs") => Ok(val as f64 * 1000.0 * 1000.0 * 60.0), // Micro sign - ("min", "μs") => Ok(val as f64 * 1000.0 * 1000.0 * 60.0), // Greek small letter - ("min", "ms") => Ok(val as f64 * 1000.0 * 60.0), - ("min", "sec") => Ok(val as f64 * 60.0), - ("min", "min") => Ok(val as f64), - ("min", "hr") => Ok(val as f64 / 60.0), - ("min", "day") => Ok(val as f64 / 60.0 / 24.0), - ("min", "wk") => Ok(val as f64 / 60.0 / 24.0 / 7.0), - ("min", "month") => Ok(val as f64 / 60.0 / 24.0 / 30.0), - ("min", "yr") => Ok(val as f64 / 60.0 / 24.0 / 365.0), - ("min", "dec") => Ok(val as f64 / 10.0 / 60.0 / 24.0 / 365.0), - - ("hr", "ns") => Ok(val as f64 * 1000.0 * 1000.0 * 1000.0 * 60.0 * 60.0), - ("hr", "us") => Ok(val as f64 * 1000.0 * 1000.0 * 60.0 * 60.0), - ("hr", "µs") => Ok(val as f64 * 1000.0 * 1000.0 * 60.0 * 60.0), // Micro sign - ("hr", "μs") => Ok(val as f64 * 1000.0 * 1000.0 * 60.0 * 60.0), // Greek small letter - ("hr", "ms") => Ok(val as f64 * 1000.0 * 60.0 * 60.0), - ("hr", "sec") => Ok(val as f64 * 60.0 * 60.0), - ("hr", "min") => Ok(val as f64 * 60.0), - ("hr", "hr") => Ok(val as f64), - ("hr", "day") => Ok(val as f64 / 24.0), - ("hr", "wk") => Ok(val as f64 / 24.0 / 7.0), - ("hr", "month") => Ok(val as f64 / 24.0 / 30.0), - ("hr", "yr") => Ok(val as f64 / 24.0 / 365.0), - ("hr", "dec") => Ok(val as f64 / 10.0 / 24.0 / 365.0), - - ("day", "ns") => Ok(val as f64 * 1000.0 * 1000.0 * 1000.0 * 60.0 * 60.0 * 24.0), - ("day", "us") => Ok(val as f64 * 1000.0 * 1000.0 * 60.0 * 60.0 * 24.0), - ("day", "µs") => Ok(val as f64 * 1000.0 * 1000.0 * 60.0 * 60.0 * 24.0), // Micro sign - ("day", "μs") => Ok(val as f64 * 1000.0 * 1000.0 * 60.0 * 60.0 * 24.0), // Greek small letter - ("day", "ms") => Ok(val as f64 * 1000.0 * 60.0 * 60.0 * 24.0), - ("day", "sec") => Ok(val as f64 * 60.0 * 60.0 * 24.0), - ("day", "min") => Ok(val as f64 * 60.0 * 24.0), - ("day", "hr") => Ok(val as f64 * 24.0), - ("day", "day") => Ok(val as f64), - ("day", "wk") => Ok(val as f64 / 7.0), - ("day", "month") => Ok(val as f64 / 30.0), - ("day", "yr") => Ok(val as f64 / 365.0), - ("day", "dec") => Ok(val as f64 / 10.0 / 365.0), - - ("wk", "ns") => Ok(val as f64 * 1000.0 * 1000.0 * 1000.0 * 60.0 * 60.0 * 24.0 * 7.0), - ("wk", "us") => Ok(val as f64 * 1000.0 * 1000.0 * 60.0 * 60.0 * 24.0 * 7.0), - ("wk", "µs") => Ok(val as f64 * 1000.0 * 1000.0 * 60.0 * 60.0 * 24.0 * 7.0), // Micro sign - ("wk", "μs") => Ok(val as f64 * 1000.0 * 1000.0 * 60.0 * 60.0 * 24.0 * 7.0), // Greek small letter - ("wk", "ms") => Ok(val as f64 * 1000.0 * 60.0 * 60.0 * 24.0 * 7.0), - ("wk", "sec") => Ok(val as f64 * 60.0 * 60.0 * 24.0 * 7.0), - ("wk", "min") => Ok(val as f64 * 60.0 * 24.0 * 7.0), - ("wk", "hr") => Ok(val as f64 * 24.0 * 7.0), - ("wk", "day") => Ok(val as f64 * 7.0), - ("wk", "wk") => Ok(val as f64), - ("wk", "month") => Ok(val as f64 / 4.0), - ("wk", "yr") => Ok(val as f64 / 52.0), - ("wk", "dec") => Ok(val as f64 / 10.0 / 52.0), - - ("month", "ns") => Ok(val as f64 * 1000.0 * 1000.0 * 1000.0 * 60.0 * 60.0 * 24.0 * 30.0), - ("month", "us") => Ok(val as f64 * 1000.0 * 1000.0 * 60.0 * 60.0 * 24.0 * 30.0), - ("month", "µs") => Ok(val as f64 * 1000.0 * 1000.0 * 60.0 * 60.0 * 24.0 * 30.0), // Micro sign - ("month", "μs") => Ok(val as f64 * 1000.0 * 1000.0 * 60.0 * 60.0 * 24.0 * 30.0), // Greek small letter - ("month", "ms") => Ok(val as f64 * 1000.0 * 60.0 * 60.0 * 24.0 * 30.0), - ("month", "sec") => Ok(val as f64 * 60.0 * 60.0 * 24.0 * 30.0), - ("month", "min") => Ok(val as f64 * 60.0 * 24.0 * 30.0), - ("month", "hr") => Ok(val as f64 * 24.0 * 30.0), - ("month", "day") => Ok(val as f64 * 30.0), - ("month", "wk") => Ok(val as f64 * 4.0), - ("month", "month") => Ok(val as f64), - ("month", "yr") => Ok(val as f64 / 12.0), - ("month", "dec") => Ok(val as f64 / 10.0 / 12.0), - - ("yr", "ns") => Ok(val as f64 * 1000.0 * 1000.0 * 1000.0 * 60.0 * 60.0 * 24.0 * 365.0), - ("yr", "us") => Ok(val as f64 * 1000.0 * 1000.0 * 60.0 * 60.0 * 24.0 * 365.0), - ("yr", "µs") => Ok(val as f64 * 1000.0 * 1000.0 * 60.0 * 60.0 * 24.0 * 365.0), // Micro sign - ("yr", "μs") => Ok(val as f64 * 1000.0 * 1000.0 * 60.0 * 60.0 * 24.0 * 365.0), // Greek small letter - ("yr", "ms") => Ok(val as f64 * 1000.0 * 60.0 * 60.0 * 24.0 * 365.0), - ("yr", "sec") => Ok(val as f64 * 60.0 * 60.0 * 24.0 * 365.0), - ("yr", "min") => Ok(val as f64 * 60.0 * 24.0 * 365.0), - ("yr", "hr") => Ok(val as f64 * 24.0 * 365.0), - ("yr", "day") => Ok(val as f64 * 365.0), - ("yr", "wk") => Ok(val as f64 * 52.0), - ("yr", "month") => Ok(val as f64 * 12.0), - ("yr", "yr") => Ok(val as f64), - ("yr", "dec") => Ok(val as f64 / 10.0), - - _ => Err(ShellError::CantConvertToDuration { - details: to_unit.to_string(), - dst_span: span, - src_span: value_span, - help: Some( - "supported units are ns, us/µs, ms, sec, min, hr, day, wk, month, yr, and dec" - .to_string(), - ), - }), - } -} - fn string_to_duration(s: &str, span: Span, value_span: Span) -> Result { if let Some(Ok(expression)) = parse_unit_value( s.as_bytes(), @@ -445,146 +197,18 @@ fn string_to_duration(s: &str, span: Span, value_span: Span) -> Result Result<(&str, i64), ShellError> { - if let Some(Ok(expression)) = parse_unit_value( - s.as_bytes(), - span, - DURATION_UNIT_GROUPS, - Type::Duration, - |x| x, - ) { - if let Expr::ValueWithUnit(value, unit) = expression.expr { - if let Expr::Int(x) = value.expr { - match unit.item { - Unit::Nanosecond => return Ok(("ns", x)), - Unit::Microsecond => return Ok(("µs", x)), - Unit::Millisecond => return Ok(("ms", x)), - Unit::Second => return Ok(("sec", x)), - Unit::Minute => return Ok(("min", x)), - Unit::Hour => return Ok(("hr", x)), - Unit::Day => return Ok(("day", x)), - Unit::Week => return Ok(("wk", x)), - - _ => return Ok(("ns", 0)), - } - } - } - } - - Err(ShellError::CantConvertToDuration { - details: s.to_string(), - dst_span: span, - src_span: value_span, - help: Some( - "supported units are ns, us/µs, ms, sec, min, hr, day, wk, month, yr, and dec" - .to_string(), - ), - }) -} - -fn action( - input: &Value, - convert_to_unit: &Option>, - float_precision: usize, - span: Span, -) -> Value { +fn action(input: &Value, span: Span) -> Value { match input { - Value::Duration { - val: val_num, - span: value_span, - } => { - if let Some(to_unit) = convert_to_unit { - let from_unit = "ns"; - let duration = *val_num; - match convert_str_from_unit_to_unit( - duration, - from_unit, - &to_unit.item, - span, - *value_span, - ) { - Ok(d) => { - let unit = if &to_unit.item == "us" { - "µs" - } else { - &to_unit.item - }; - if d.fract() == 0.0 { - Value::String { - val: format!("{} {}", d, unit), - span: *value_span, - } - } else { - Value::String { - val: format!("{:.float_precision$} {}", d, unit), - span: *value_span, - } - } - } - Err(e) => Value::Error { error: Box::new(e) }, - } - } else { - input.clone() - } - } + Value::Duration { .. } => input.clone(), Value::String { val, span: value_span, - } => { - if let Some(to_unit) = convert_to_unit { - if let Ok(dur) = string_to_unit_duration(val, span, *value_span) { - let from_unit = dur.0; - let duration = dur.1; - match convert_str_from_unit_to_unit( - duration, - from_unit, - &to_unit.item, - span, - *value_span, - ) { - Ok(d) => { - let unit = if &to_unit.item == "us" { - "µs" - } else { - &to_unit.item - }; - if d.fract() == 0.0 { - Value::String { - val: format!("{} {}", d, unit), - span: *value_span, - } - } else { - Value::String { - val: format!("{:.float_precision$} {}", d, unit), - span: *value_span, - } - } - } - Err(e) => Value::Error { error: Box::new(e) }, - } - } else { - Value::Error { - error: Box::new(ShellError::CantConvert { - to_type: "string".into(), - from_type: "duration".into(), - span, - help: None, - }), - } - } - } else { - match string_to_duration(val, span, *value_span) { - Ok(val) => Value::Duration { val, span }, - Err(error) => Value::Error { - error: Box::new(error), - }, - } - } - } + } => match string_to_duration(val, span, *value_span) { + Ok(val) => Value::Duration { val, span }, + Err(error) => Value::Error { + error: Box::new(error), + }, + }, // Propagate errors by explicitly matching them before the final case. Value::Error { .. } => input.clone(), other => Value::Error { @@ -614,9 +238,8 @@ mod test { let span = Span::new(0, 2); let word = Value::test_string("3ns"); let expected = Value::Duration { val: 3, span }; - let convert_duration = None; - let actual = action(&word, &convert_duration, 2, span); + let actual = action(&word, span); assert_eq!(actual, expected); } @@ -628,9 +251,8 @@ mod test { val: 4 * 1000, span, }; - let convert_duration = None; - let actual = action(&word, &convert_duration, 2, span); + let actual = action(&word, span); assert_eq!(actual, expected); } @@ -642,9 +264,8 @@ mod test { val: 4 * 1000, span, }; - let convert_duration = None; - let actual = action(&word, &convert_duration, 2, span); + let actual = action(&word, span); assert_eq!(actual, expected); } @@ -656,9 +277,8 @@ mod test { val: 4 * 1000, span, }; - let convert_duration = None; - let actual = action(&word, &convert_duration, 2, span); + let actual = action(&word, span); assert_eq!(actual, expected); } @@ -670,9 +290,8 @@ mod test { val: 5 * 1000 * 1000, span, }; - let convert_duration = None; - let actual = action(&word, &convert_duration, 2, span); + let actual = action(&word, span); assert_eq!(actual, expected); } @@ -684,9 +303,8 @@ mod test { val: 1000 * 1000 * 1000, span, }; - let convert_duration = None; - let actual = action(&word, &convert_duration, 2, span); + let actual = action(&word, span); assert_eq!(actual, expected); } @@ -698,9 +316,8 @@ mod test { val: 7 * 60 * 1000 * 1000 * 1000, span, }; - let convert_duration = None; - let actual = action(&word, &convert_duration, 2, span); + let actual = action(&word, span); assert_eq!(actual, expected); } @@ -712,9 +329,8 @@ mod test { val: 42 * 60 * 60 * 1000 * 1000 * 1000, span, }; - let convert_duration = None; - let actual = action(&word, &convert_duration, 2, span); + let actual = action(&word, span); assert_eq!(actual, expected); } @@ -726,9 +342,8 @@ mod test { val: 123 * 24 * 60 * 60 * 1000 * 1000 * 1000, span, }; - let convert_duration = None; - let actual = action(&word, &convert_duration, 2, span); + let actual = action(&word, span); assert_eq!(actual, expected); } @@ -740,9 +355,8 @@ mod test { val: 3 * 7 * 24 * 60 * 60 * 1000 * 1000 * 1000, span, }; - let convert_duration = None; - let actual = action(&word, &convert_duration, 2, span); + let actual = action(&word, span); assert_eq!(actual, expected); } }