From 67b6188b19a5279eaf13a33bfee5b44583c3b9ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Riegel?= <96702577+LoicRiegel@users.noreply.github.com> Date: Thu, 3 Apr 2025 14:05:18 +0200 Subject: [PATCH] feat: into duration accepts floats (#15297) Issue #9887 which can be closed after this is merged. # Description This allows the "into duration" command to accept floats as inputs. Examples: image image **How it works:** Using strings, like `"1.234sec" | into duration`, is already working, so if a user inputs `1.234 | into duration --sec`, I just convert this back to a string and use the previous conversion functions. **Limitations:** there are some limitation to using floats, but it's a general limitation that is already present for other use cases: - only 3 digits are taken into account in the decimal part - floating durations in nano seconds are always floored and not rounded image # User-Facing Changes Users can inject floats with `into duration` # Tests + Formatting cargo fmt and clippy OK Tests OK # After Submitting The example I added will automatically become part of the doc, I think that's enough for documentation. --- .../src/conversions/into/duration.rs | 50 +++++++++++++------ 1 file changed, 35 insertions(+), 15 deletions(-) diff --git a/crates/nu-command/src/conversions/into/duration.rs b/crates/nu-command/src/conversions/into/duration.rs index 129188f839..8c92c22841 100644 --- a/crates/nu-command/src/conversions/into/duration.rs +++ b/crates/nu-command/src/conversions/into/duration.rs @@ -15,6 +15,7 @@ impl Command for IntoDuration { Signature::build("into duration") .input_output_types(vec![ (Type::Int, Type::Duration), + (Type::Float, Type::Duration), (Type::String, Type::Duration), (Type::Duration, Type::Duration), (Type::table(), Type::table()), @@ -109,6 +110,11 @@ impl Command for IntoDuration { example: "1_234 | into duration --unit ms", result: Some(Value::test_duration(1_234 * 1_000_000)), }, + Example { + description: "Convert a floating point number of an arbitrary unit to duration", + example: "1.234 | into duration --unit sec", + result: Some(Value::test_duration(1_234 * 1_000_000)), + }, ] } } @@ -236,22 +242,22 @@ fn action(input: &Value, unit: &str, span: Span) -> Value { let value_span = input.span(); match input { Value::Duration { .. } => input.clone(), - Value::String { val, .. } => match compound_to_duration(val, value_span) { - Ok(val) => Value::duration(val, span), - Err(error) => Value::error(error, span), - }, + Value::String { val, .. } => { + if let Ok(num) = val.parse::() { + let ns = unit_to_ns_factor(unit); + return Value::duration((num * (ns as f64)) as i64, span); + } + match compound_to_duration(val, value_span) { + Ok(val) => Value::duration(val, span), + Err(error) => Value::error(error, span), + } + } + Value::Float { val, .. } => { + let ns = unit_to_ns_factor(unit); + Value::duration((*val * (ns as f64)) as i64, span) + } Value::Int { val, .. } => { - let ns = match unit { - "ns" => 1, - "us" | "µs" => 1_000, - "ms" => 1_000_000, - "sec" => NS_PER_SEC, - "min" => NS_PER_SEC * 60, - "hr" => NS_PER_SEC * 60 * 60, - "day" => NS_PER_SEC * 60 * 60 * 24, - "wk" => NS_PER_SEC * 60 * 60 * 24 * 7, - _ => 0, - }; + let ns = unit_to_ns_factor(unit); Value::duration(*val * ns, span) } // Propagate errors by explicitly matching them before the final case. @@ -268,6 +274,20 @@ fn action(input: &Value, unit: &str, span: Span) -> Value { } } +fn unit_to_ns_factor(unit: &str) -> i64 { + match unit { + "ns" => 1, + "us" | "µs" => 1_000, + "ms" => 1_000_000, + "sec" => NS_PER_SEC, + "min" => NS_PER_SEC * 60, + "hr" => NS_PER_SEC * 60 * 60, + "day" => NS_PER_SEC * 60 * 60 * 24, + "wk" => NS_PER_SEC * 60 * 60 * 24 * 7, + _ => 0, + } +} + #[cfg(test)] mod test { use super::*;