forked from extern/nushell
allow into duration
to take an integer amount of ns (#10286)
related to - https://discord.com/channels/601130461678272522/615329862395101194/1149717458786197524 # Description because `1_234 | into datetime` takes an integer number of `ns` and `1_234 | into filesize` takes an integer amount of bytes, i think `1_234 | into duration` should also be valid and see `1_234` as an integer amount of `ns` 😋 # User-Facing Changes ## before either ```nushell 1234 | into string | $in ++ "ns" | into duration ``` ```nushell 1234 | $"($in)ns" | into duration ``` or ```nushell 1234 * 1ns ``` and ```nushell > 1_234 | into duration Error: nu::parser::input_type_mismatch × Command does not support int input. ╭─[entry #2:1:1] 1 │ 1_234 | into duration · ──────┬────── · ╰── command doesn't support int input ╰──── ``` ## after ```nushell > 1_234 | into duration 1µs 234ns ``` # Tests + Formatting new example test ```rust Example { description: "Convert a number of ns to duration", example: "1_234_567 | into duration", result: Some(Value::duration(1_234_567, span)), } ``` # After Submitting
This commit is contained in:
parent
40eca52ed5
commit
17abbdf6e0
@ -19,6 +19,7 @@ impl Command for SubCommand {
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("into duration")
|
||||
.input_output_types(vec![
|
||||
(Type::Int, Type::Duration),
|
||||
(Type::String, Type::Duration),
|
||||
(Type::Duration, Type::Duration),
|
||||
(Type::Table(vec![]), Type::Table(vec![])),
|
||||
@ -26,6 +27,12 @@ impl Command for SubCommand {
|
||||
//(Type::Record(vec![]), Type::Record(vec![])),
|
||||
])
|
||||
//.allow_variants_without_examples(true)
|
||||
.named(
|
||||
"unit",
|
||||
SyntaxShape::String,
|
||||
"Unit to convert number into (will have an effect only with integer input)",
|
||||
Some('u'),
|
||||
)
|
||||
.rest(
|
||||
"rest",
|
||||
SyntaxShape::CellPath,
|
||||
@ -107,6 +114,16 @@ impl Command for SubCommand {
|
||||
example: "420sec | into duration",
|
||||
result: Some(Value::duration(7 * 60 * NS_PER_SEC, span)),
|
||||
},
|
||||
Example {
|
||||
description: "Convert a number of ns to duration",
|
||||
example: "1_234_567 | into duration",
|
||||
result: Some(Value::duration(1_234_567, span)),
|
||||
},
|
||||
Example {
|
||||
description: "Convert a number of an arbitraty unit to duration",
|
||||
example: "1_234 | into duration --unit ms",
|
||||
result: Some(Value::duration(1_234 * 1_000_000, span)),
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
@ -123,15 +140,39 @@ fn into_duration(
|
||||
};
|
||||
let column_paths: Vec<CellPath> = call.rest(engine_state, stack, 0)?;
|
||||
|
||||
let unit = match call.get_flag::<String>(engine_state, stack, "unit")? {
|
||||
Some(sep) => {
|
||||
if ["ns", "us", "µs", "ms", "sec", "min", "hr", "day", "wk"]
|
||||
.iter()
|
||||
.any(|d| d == &sep)
|
||||
{
|
||||
sep
|
||||
} else {
|
||||
return Err(ShellError::CantConvertToDuration {
|
||||
details: sep,
|
||||
dst_span: span,
|
||||
src_span: span,
|
||||
help: Some(
|
||||
"supported units are ns, us/µs, ms, sec, min, hr, day, and wk".to_string(),
|
||||
),
|
||||
});
|
||||
}
|
||||
}
|
||||
None => "ns".to_string(),
|
||||
};
|
||||
|
||||
input.map(
|
||||
move |v| {
|
||||
if column_paths.is_empty() {
|
||||
action(&v, span)
|
||||
action(&v, &unit.clone(), span)
|
||||
} else {
|
||||
let unitclone = &unit.clone();
|
||||
let mut ret = v;
|
||||
for path in &column_paths {
|
||||
let r =
|
||||
ret.update_cell_path(&path.members, Box::new(move |old| action(old, span)));
|
||||
let r = ret.update_cell_path(
|
||||
&path.members,
|
||||
Box::new(move |old| action(old, unitclone, span)),
|
||||
);
|
||||
if let Err(error) = r {
|
||||
return Value::error(error, span);
|
||||
}
|
||||
@ -202,7 +243,7 @@ fn string_to_duration(s: &str, span: Span) -> Result<i64, ShellError> {
|
||||
})
|
||||
}
|
||||
|
||||
fn action(input: &Value, span: Span) -> Value {
|
||||
fn action(input: &Value, unit: &str, span: Span) -> Value {
|
||||
let value_span = input.span();
|
||||
match input {
|
||||
Value::Duration { .. } => input.clone(),
|
||||
@ -210,6 +251,20 @@ fn action(input: &Value, span: Span) -> Value {
|
||||
Ok(val) => Value::duration(val, span),
|
||||
Err(error) => Value::error(error, 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,
|
||||
};
|
||||
Value::duration(*val * ns, span)
|
||||
}
|
||||
// Propagate errors by explicitly matching them before the final case.
|
||||
Value::Error { .. } => input.clone(),
|
||||
other => Value::error(
|
||||
@ -253,7 +308,11 @@ mod test {
|
||||
#[case("14ns 3hr 17sec", 14 + 3 * 3600 * NS_PER_SEC + 17 * NS_PER_SEC)] // compound string with units in random order
|
||||
|
||||
fn turns_string_to_duration(#[case] phrase: &str, #[case] expected_duration_val: i64) {
|
||||
let actual = action(&Value::test_string(phrase), Span::new(0, phrase.len()));
|
||||
let actual = action(
|
||||
&Value::test_string(phrase),
|
||||
"ns",
|
||||
Span::new(0, phrase.len()),
|
||||
);
|
||||
match actual {
|
||||
Value::Duration {
|
||||
val: observed_val, ..
|
||||
|
Loading…
Reference in New Issue
Block a user