add the ability to convert durations (#6723)

* add the ability to convert durations

* add conversion to floats if necessary
This commit is contained in:
Darren Schroeder 2022-10-14 11:46:48 -05:00 committed by GitHub
parent 804b155035
commit 1344ae3a65
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -114,14 +114,21 @@ impl Command for SubCommand {
}), }),
}, },
Example { Example {
description: "Convert string to a named duration", description: "Convert string to the requested duration as a string",
example: "'7min' | into duration --convert sec", example: "'7min' | into duration --convert sec",
result: Some(Value::String { result: Some(Value::String {
val: "420 sec".to_string(), val: "420 sec".to_string(),
span, span,
}), }),
}, },
Example {
description: "Convert duration to the requested duration as a string",
example: "420sec | into duration --convert min",
result: Some(Value::String {
val: "7 min".to_string(),
span,
}),
},
] ]
} }
} }
@ -135,18 +142,20 @@ fn into_duration(
let head = call.head; let head = call.head;
let convert_to_unit: Option<Spanned<String>> = call.get_flag(engine_state, stack, "convert")?; let convert_to_unit: Option<Spanned<String>> = call.get_flag(engine_state, stack, "convert")?;
let column_paths: Vec<CellPath> = call.rest(engine_state, stack, 0)?; let column_paths: Vec<CellPath> = call.rest(engine_state, stack, 0)?;
let config = engine_state.get_config();
let float_precision = config.float_precision as usize;
input.map( input.map(
move |v| { move |v| {
if column_paths.is_empty() { if column_paths.is_empty() {
action(&v, &convert_to_unit, head) action(&v, &convert_to_unit, float_precision, head)
} else { } else {
let mut ret = v; let mut ret = v;
for path in &column_paths { for path in &column_paths {
let d = convert_to_unit.clone(); let d = convert_to_unit.clone();
let r = ret.update_cell_path( let r = ret.update_cell_path(
&path.members, &path.members,
Box::new(move |old| action(old, &d, head)), Box::new(move |old| action(old, &d, float_precision, head)),
); );
if let Err(error) = r { if let Err(error) = r {
return Value::Error { error }; return Value::Error { error };
@ -166,127 +175,129 @@ fn convert_str_from_unit_to_unit(
to_unit: &str, to_unit: &str,
span: Span, span: Span,
value_span: Span, value_span: Span,
) -> Result<i64, ShellError> { ) -> Result<f64, ShellError> {
match (from_unit, to_unit) { match (from_unit, to_unit) {
("ns", "ns") => Ok(val), ("ns", "ns") => Ok(val as f64),
("ns", "us") => Ok(val / 1000), ("ns", "us") => Ok(val as f64 / 1000.0),
("ns", "ms") => Ok(val / 1000 / 1000), ("ns", "ms") => Ok(val as f64 / 1000.0 / 1000.0),
("ns", "sec") => Ok(val / 1000 / 1000 / 1000), ("ns", "sec") => Ok(val as f64 / 1000.0 / 1000.0 / 1000.0),
("ns", "min") => Ok(val / 1000 / 1000 / 1000 / 60), ("ns", "min") => Ok(val as f64 / 1000.0 / 1000.0 / 1000.0 / 60.0),
("ns", "hr") => Ok(val / 1000 / 1000 / 1000 / 60 / 60), ("ns", "hr") => Ok(val as f64 / 1000.0 / 1000.0 / 1000.0 / 60.0 / 60.0),
("ns", "day") => Ok(val / 1000 / 1000 / 1000 / 60 / 60 / 24), ("ns", "day") => Ok(val as f64 / 1000.0 / 1000.0 / 1000.0 / 60.0 / 60.0 / 24.0),
("ns", "wk") => Ok(val / 1000 / 1000 / 1000 / 60 / 60 / 24 / 7), ("ns", "wk") => Ok(val as f64 / 1000.0 / 1000.0 / 1000.0 / 60.0 / 60.0 / 24.0 / 7.0),
("ns", "month") => Ok(val / 1000 / 1000 / 1000 / 60 / 60 / 24 / 30), ("ns", "month") => Ok(val as f64 / 1000.0 / 1000.0 / 1000.0 / 60.0 / 60.0 / 24.0 / 30.0),
("ns", "yr") => Ok(val / 1000 / 1000 / 1000 / 60 / 60 / 24 / 365), ("ns", "yr") => Ok(val as f64 / 1000.0 / 1000.0 / 1000.0 / 60.0 / 60.0 / 24.0 / 365.0),
("ns", "dec") => Ok(val / 10 / 1000 / 1000 / 1000 / 60 / 60 / 24 / 365), ("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 * 1000), ("us", "ns") => Ok(val as f64 * 1000.0),
("us", "us") => Ok(val), ("us", "us") => Ok(val as f64),
("us", "ms") => Ok(val / 1000), ("us", "ms") => Ok(val as f64 / 1000.0),
("us", "sec") => Ok(val / 1000 / 1000), ("us", "sec") => Ok(val as f64 / 1000.0 / 1000.0),
("us", "min") => Ok(val / 1000 / 1000 / 60), ("us", "min") => Ok(val as f64 / 1000.0 / 1000.0 / 60.0),
("us", "hr") => Ok(val / 1000 / 1000 / 60 / 60), ("us", "hr") => Ok(val as f64 / 1000.0 / 1000.0 / 60.0 / 60.0),
("us", "day") => Ok(val / 1000 / 1000 / 60 / 60 / 24), ("us", "day") => Ok(val as f64 / 1000.0 / 1000.0 / 60.0 / 60.0 / 24.0),
("us", "wk") => Ok(val / 1000 / 1000 / 60 / 60 / 24 / 7), ("us", "wk") => Ok(val as f64 / 1000.0 / 1000.0 / 60.0 / 60.0 / 24.0 / 7.0),
("us", "month") => Ok(val / 1000 / 1000 / 60 / 60 / 24 / 30), ("us", "month") => Ok(val as f64 / 1000.0 / 1000.0 / 60.0 / 60.0 / 24.0 / 30.0),
("us", "yr") => Ok(val / 1000 / 1000 / 60 / 60 / 24 / 365), ("us", "yr") => Ok(val as f64 / 1000.0 / 1000.0 / 60.0 / 60.0 / 24.0 / 365.0),
("us", "dec") => Ok(val / 10 / 1000 / 1000 / 60 / 60 / 24 / 365), ("us", "dec") => Ok(val as f64 / 10.0 / 1000.0 / 1000.0 / 60.0 / 60.0 / 24.0 / 365.0),
("ms", "ns") => Ok(val * 1000 * 1000), ("ms", "ns") => Ok(val as f64 * 1000.0 * 1000.0),
("ms", "us") => Ok(val * 1000), ("ms", "us") => Ok(val as f64 * 1000.0),
("ms", "ms") => Ok(val), ("ms", "ms") => Ok(val as f64),
("ms", "sec") => Ok(val / 1000), ("ms", "sec") => Ok(val as f64 / 1000.0),
("ms", "min") => Ok(val / 1000 / 60), ("ms", "min") => Ok(val as f64 / 1000.0 / 60.0),
("ms", "hr") => Ok(val / 1000 / 60 / 60), ("ms", "hr") => Ok(val as f64 / 1000.0 / 60.0 / 60.0),
("ms", "day") => Ok(val / 1000 / 60 / 60 / 24), ("ms", "day") => Ok(val as f64 / 1000.0 / 60.0 / 60.0 / 24.0),
("ms", "wk") => Ok(val / 1000 / 60 / 60 / 24 / 7), ("ms", "wk") => Ok(val as f64 / 1000.0 / 60.0 / 60.0 / 24.0 / 7.0),
("ms", "month") => Ok(val / 1000 / 60 / 60 / 24 / 30), ("ms", "month") => Ok(val as f64 / 1000.0 / 60.0 / 60.0 / 24.0 / 30.0),
("ms", "yr") => Ok(val / 1000 / 60 / 60 / 24 / 365), ("ms", "yr") => Ok(val as f64 / 1000.0 / 60.0 / 60.0 / 24.0 / 365.0),
("ms", "dec") => Ok(val / 10 / 1000 / 60 / 60 / 24 / 365), ("ms", "dec") => Ok(val as f64 / 10.0 / 1000.0 / 60.0 / 60.0 / 24.0 / 365.0),
("sec", "ns") => Ok(val * 1000 * 1000 * 1000), ("sec", "ns") => Ok(val as f64 * 1000.0 * 1000.0 * 1000.0),
("sec", "us") => Ok(val * 1000 * 1000), ("sec", "us") => Ok(val as f64 * 1000.0 * 1000.0),
("sec", "ms") => Ok(val * 1000), ("sec", "ms") => Ok(val as f64 * 1000.0),
("sec", "sec") => Ok(val), ("sec", "sec") => Ok(val as f64),
("sec", "min") => Ok(val / 60), ("sec", "min") => Ok(val as f64 / 60.0),
("sec", "hr") => Ok(val / 60 / 60), ("sec", "hr") => Ok(val as f64 / 60.0 / 60.0),
("sec", "day") => Ok(val / 60 / 60 / 24), ("sec", "day") => Ok(val as f64 / 60.0 / 60.0 / 24.0),
("sec", "wk") => Ok(val / 60 / 60 / 24 / 7), ("sec", "wk") => Ok(val as f64 / 60.0 / 60.0 / 24.0 / 7.0),
("sec", "month") => Ok(val / 60 / 60 / 24 / 30), ("sec", "month") => Ok(val as f64 / 60.0 / 60.0 / 24.0 / 30.0),
("sec", "yr") => Ok(val / 60 / 60 / 24 / 365), ("sec", "yr") => Ok(val as f64 / 60.0 / 60.0 / 24.0 / 365.0),
("sec", "dec") => Ok(val / 10 / 60 / 60 / 24 / 365), ("sec", "dec") => Ok(val as f64 / 10.0 / 60.0 / 60.0 / 24.0 / 365.0),
("min", "ns") => Ok(val * 1000 * 1000 * 1000 * 60), ("min", "ns") => Ok(val as f64 * 1000.0 * 1000.0 * 1000.0 * 60.0),
("min", "us") => Ok(val * 1000 * 1000 * 60), ("min", "us") => Ok(val as f64 * 1000.0 * 1000.0 * 60.0),
("min", "ms") => Ok(val * 1000 * 60), ("min", "ms") => Ok(val as f64 * 1000.0 * 60.0),
("min", "sec") => Ok(val * 60), ("min", "sec") => Ok(val as f64 * 60.0),
("min", "min") => Ok(val), ("min", "min") => Ok(val as f64),
("min", "hr") => Ok(val / 60), ("min", "hr") => Ok(val as f64 / 60.0),
("min", "day") => Ok(val / 60 / 24), ("min", "day") => Ok(val as f64 / 60.0 / 24.0),
("min", "wk") => Ok(val / 60 / 24 / 7), ("min", "wk") => Ok(val as f64 / 60.0 / 24.0 / 7.0),
("min", "month") => Ok(val / 60 / 24 / 30), ("min", "month") => Ok(val as f64 / 60.0 / 24.0 / 30.0),
("min", "yr") => Ok(val / 60 / 24 / 365), ("min", "yr") => Ok(val as f64 / 60.0 / 24.0 / 365.0),
("min", "dec") => Ok(val / 10 / 60 / 24 / 365), ("min", "dec") => Ok(val as f64 / 10.0 / 60.0 / 24.0 / 365.0),
("hr", "ns") => Ok(val * 1000 * 1000 * 1000 * 60 * 60), ("hr", "ns") => Ok(val as f64 * 1000.0 * 1000.0 * 1000.0 * 60.0 * 60.0),
("hr", "us") => Ok(val * 1000 * 1000 * 60 * 60), ("hr", "us") => Ok(val as f64 * 1000.0 * 1000.0 * 60.0 * 60.0),
("hr", "ms") => Ok(val * 1000 * 60 * 60), ("hr", "ms") => Ok(val as f64 * 1000.0 * 60.0 * 60.0),
("hr", "sec") => Ok(val * 60 * 60), ("hr", "sec") => Ok(val as f64 * 60.0 * 60.0),
("hr", "min") => Ok(val * 60), ("hr", "min") => Ok(val as f64 * 60.0),
("hr", "hr") => Ok(val), ("hr", "hr") => Ok(val as f64),
("hr", "day") => Ok(val / 24), ("hr", "day") => Ok(val as f64 / 24.0),
("hr", "wk") => Ok(val / 24 / 7), ("hr", "wk") => Ok(val as f64 / 24.0 / 7.0),
("hr", "month") => Ok(val / 24 / 30), ("hr", "month") => Ok(val as f64 / 24.0 / 30.0),
("hr", "yr") => Ok(val / 24 / 365), ("hr", "yr") => Ok(val as f64 / 24.0 / 365.0),
("hr", "dec") => Ok(val / 10 / 24 / 365), ("hr", "dec") => Ok(val as f64 / 10.0 / 24.0 / 365.0),
("day", "ns") => Ok(val * 1000 * 1000 * 1000 * 60 * 60 * 24), ("day", "ns") => Ok(val as f64 * 1000.0 * 1000.0 * 1000.0 * 60.0 * 60.0 * 24.0),
("day", "us") => Ok(val * 1000 * 1000 * 60 * 60 * 24), ("day", "us") => Ok(val as f64 * 1000.0 * 1000.0 * 60.0 * 60.0 * 24.0),
("day", "ms") => Ok(val * 1000 * 60 * 60 * 24), ("day", "ms") => Ok(val as f64 * 1000.0 * 60.0 * 60.0 * 24.0),
("day", "sec") => Ok(val * 60 * 60 * 24), ("day", "sec") => Ok(val as f64 * 60.0 * 60.0 * 24.0),
("day", "min") => Ok(val * 60 * 24), ("day", "min") => Ok(val as f64 * 60.0 * 24.0),
("day", "hr") => Ok(val * 24), ("day", "hr") => Ok(val as f64 * 24.0),
("day", "day") => Ok(val), ("day", "day") => Ok(val as f64),
("day", "wk") => Ok(val / 7), ("day", "wk") => Ok(val as f64 / 7.0),
("day", "month") => Ok(val / 30), ("day", "month") => Ok(val as f64 / 30.0),
("day", "yr") => Ok(val / 365), ("day", "yr") => Ok(val as f64 / 365.0),
("day", "dec") => Ok(val / 10 / 365), ("day", "dec") => Ok(val as f64 / 10.0 / 365.0),
("wk", "ns") => Ok(val * 1000 * 1000 * 1000 * 60 * 60 * 24 * 7), ("wk", "ns") => Ok(val as f64 * 1000.0 * 1000.0 * 1000.0 * 60.0 * 60.0 * 24.0 * 7.0),
("wk", "us") => Ok(val * 1000 * 1000 * 60 * 60 * 24 * 7), ("wk", "us") => Ok(val as f64 * 1000.0 * 1000.0 * 60.0 * 60.0 * 24.0 * 7.0),
("wk", "ms") => Ok(val * 1000 * 60 * 60 * 24 * 7), ("wk", "ms") => Ok(val as f64 * 1000.0 * 60.0 * 60.0 * 24.0 * 7.0),
("wk", "sec") => Ok(val * 60 * 60 * 24 * 7), ("wk", "sec") => Ok(val as f64 * 60.0 * 60.0 * 24.0 * 7.0),
("wk", "min") => Ok(val * 60 * 24 * 7), ("wk", "min") => Ok(val as f64 * 60.0 * 24.0 * 7.0),
("wk", "hr") => Ok(val * 24 * 7), ("wk", "hr") => Ok(val as f64 * 24.0 * 7.0),
("wk", "day") => Ok(val * 7), ("wk", "day") => Ok(val as f64 * 7.0),
("wk", "wk") => Ok(val), ("wk", "wk") => Ok(val as f64),
("wk", "month") => Ok(val / 4), ("wk", "month") => Ok(val as f64 / 4.0),
("wk", "yr") => Ok(val / 52), ("wk", "yr") => Ok(val as f64 / 52.0),
("wk", "dec") => Ok(val / 10 / 52), ("wk", "dec") => Ok(val as f64 / 10.0 / 52.0),
("month", "ns") => Ok(val * 1000 * 1000 * 1000 * 60 * 60 * 24 * 30), ("month", "ns") => Ok(val as f64 * 1000.0 * 1000.0 * 1000.0 * 60.0 * 60.0 * 24.0 * 30.0),
("month", "us") => Ok(val * 1000 * 1000 * 60 * 60 * 24 * 30), ("month", "us") => Ok(val as f64 * 1000.0 * 1000.0 * 60.0 * 60.0 * 24.0 * 30.0),
("month", "ms") => Ok(val * 1000 * 60 * 60 * 24 * 30), ("month", "ms") => Ok(val as f64 * 1000.0 * 60.0 * 60.0 * 24.0 * 30.0),
("month", "sec") => Ok(val * 60 * 60 * 24 * 30), ("month", "sec") => Ok(val as f64 * 60.0 * 60.0 * 24.0 * 30.0),
("month", "min") => Ok(val * 60 * 24 * 30), ("month", "min") => Ok(val as f64 * 60.0 * 24.0 * 30.0),
("month", "hr") => Ok(val * 24 * 30), ("month", "hr") => Ok(val as f64 * 24.0 * 30.0),
("month", "day") => Ok(val * 30), ("month", "day") => Ok(val as f64 * 30.0),
("month", "wk") => Ok(val * 4), ("month", "wk") => Ok(val as f64 * 4.0),
("month", "month") => Ok(val), ("month", "month") => Ok(val as f64),
("month", "yr") => Ok(val / 12), ("month", "yr") => Ok(val as f64 / 12.0),
("month", "dec") => Ok(val / 10 / 12), ("month", "dec") => Ok(val as f64 / 10.0 / 12.0),
("yr", "ns") => Ok(val * 1000 * 1000 * 1000 * 60 * 60 * 24 * 365), ("yr", "ns") => Ok(val as f64 * 1000.0 * 1000.0 * 1000.0 * 60.0 * 60.0 * 24.0 * 365.0),
("yr", "us") => Ok(val * 1000 * 1000 * 60 * 60 * 24 * 365), ("yr", "us") => Ok(val as f64 * 1000.0 * 1000.0 * 60.0 * 60.0 * 24.0 * 365.0),
("yr", "ms") => Ok(val * 1000 * 60 * 60 * 24 * 365), ("yr", "ms") => Ok(val as f64 * 1000.0 * 60.0 * 60.0 * 24.0 * 365.0),
("yr", "sec") => Ok(val * 60 * 60 * 24 * 365), ("yr", "sec") => Ok(val as f64 * 60.0 * 60.0 * 24.0 * 365.0),
("yr", "min") => Ok(val * 60 * 24 * 365), ("yr", "min") => Ok(val as f64 * 60.0 * 24.0 * 365.0),
("yr", "hr") => Ok(val * 24 * 365), ("yr", "hr") => Ok(val as f64 * 24.0 * 365.0),
("yr", "day") => Ok(val * 365), ("yr", "day") => Ok(val as f64 * 365.0),
("yr", "wk") => Ok(val * 52), ("yr", "wk") => Ok(val as f64 * 52.0),
("yr", "month") => Ok(val * 12), ("yr", "month") => Ok(val as f64 * 12.0),
("yr", "yr") => Ok(val), ("yr", "yr") => Ok(val as f64),
("yr", "dec") => Ok(val / 10), ("yr", "dec") => Ok(val as f64 / 10.0),
_ => Err(ShellError::CantConvertWithValue( _ => Err(ShellError::CantConvertWithValue(
"string duration".to_string(), "string duration".to_string(),
@ -369,19 +380,41 @@ fn string_to_unit_duration(
)) ))
} }
fn action(input: &Value, convert_to_unit: &Option<Spanned<String>>, span: Span) -> Value { fn action(
input: &Value,
convert_to_unit: &Option<Spanned<String>>,
float_precision: usize,
span: Span,
) -> Value {
match input { match input {
Value::Duration { Value::Duration {
val: _val_num, val: val_num,
span: _value_span, span: value_span,
} => { } => {
if let Some(_to_unit) = convert_to_unit { if let Some(to_unit) = convert_to_unit {
Value::Error { let from_unit = "ns";
error: ShellError::UnsupportedInput( let duration = *val_num;
"Cannot convert from a Value::Duration right now. Try making it a string." match convert_str_from_unit_to_unit(
.into(), duration,
span, from_unit,
), &to_unit.item,
span,
*value_span,
) {
Ok(d) => {
if d.fract() == 0.0 {
Value::String {
val: format!("{} {}", d, &to_unit.item),
span: *value_span,
}
} else {
Value::String {
val: format!("{:.float_precision$} {}", d, &to_unit.item),
span: *value_span,
}
}
}
Err(e) => Value::Error { error: e },
} }
} else { } else {
input.clone() input.clone()
@ -402,10 +435,19 @@ fn action(input: &Value, convert_to_unit: &Option<Spanned<String>>, span: Span)
span, span,
*value_span, *value_span,
) { ) {
Ok(d) => Value::String { Ok(d) => {
val: format!("{} {}", d, &to_unit.item), if d.fract() == 0.0 {
span: *value_span, Value::String {
}, val: format!("{} {}", d, &to_unit.item),
span: *value_span,
}
} else {
Value::String {
val: format!("{:.float_precision$} {}", d, &to_unit.item),
span: *value_span,
}
}
}
Err(e) => Value::Error { error: e }, Err(e) => Value::Error { error: e },
} }
} else { } else {
@ -450,7 +492,7 @@ mod test {
let expected = Value::Duration { val: 3, span }; let expected = Value::Duration { val: 3, span };
let convert_duration = None; let convert_duration = None;
let actual = action(&word, &convert_duration, span); let actual = action(&word, &convert_duration, 2, span);
assert_eq!(actual, expected); assert_eq!(actual, expected);
} }
@ -464,7 +506,7 @@ mod test {
}; };
let convert_duration = None; let convert_duration = None;
let actual = action(&word, &convert_duration, span); let actual = action(&word, &convert_duration, 2, span);
assert_eq!(actual, expected); assert_eq!(actual, expected);
} }
@ -478,7 +520,7 @@ mod test {
}; };
let convert_duration = None; let convert_duration = None;
let actual = action(&word, &convert_duration, span); let actual = action(&word, &convert_duration, 2, span);
assert_eq!(actual, expected); assert_eq!(actual, expected);
} }
@ -492,7 +534,7 @@ mod test {
}; };
let convert_duration = None; let convert_duration = None;
let actual = action(&word, &convert_duration, span); let actual = action(&word, &convert_duration, 2, span);
assert_eq!(actual, expected); assert_eq!(actual, expected);
} }
@ -506,7 +548,7 @@ mod test {
}; };
let convert_duration = None; let convert_duration = None;
let actual = action(&word, &convert_duration, span); let actual = action(&word, &convert_duration, 2, span);
assert_eq!(actual, expected); assert_eq!(actual, expected);
} }
@ -520,7 +562,7 @@ mod test {
}; };
let convert_duration = None; let convert_duration = None;
let actual = action(&word, &convert_duration, span); let actual = action(&word, &convert_duration, 2, span);
assert_eq!(actual, expected); assert_eq!(actual, expected);
} }
@ -534,7 +576,7 @@ mod test {
}; };
let convert_duration = None; let convert_duration = None;
let actual = action(&word, &convert_duration, span); let actual = action(&word, &convert_duration, 2, span);
assert_eq!(actual, expected); assert_eq!(actual, expected);
} }
@ -548,7 +590,7 @@ mod test {
}; };
let convert_duration = None; let convert_duration = None;
let actual = action(&word, &convert_duration, span); let actual = action(&word, &convert_duration, 2, span);
assert_eq!(actual, expected); assert_eq!(actual, expected);
} }
} }