Allow parsing of mu (µ) character for durations (issue #8614) (#8647)

# Description
This is to resolve the issue
[8614](https://github.com/nushell/nushell/issues/8614).
It allows the parsing of the mu (µ) character for durations, so you can
type `10µs`, and it correctly outputs, whilst maintaining the current
`us` parsing as well.

It also forces `durations` to be entered in lower case. 


![image](https://user-images.githubusercontent.com/44570273/228217360-57ebc902-cec5-4683-910e-0b18fbe160b1.png)
(The bottom one `1sec | into duration --convert us` looks like an
existing bug, where converting to `us` outputs `us` rather than `µs`)

# User-Facing Changes

Allows the user to parse durations in µs
Forces `durations` to be entered in lower case rather than any case, and
will error if not in lower case.

# Tests + Formatting

Don't forget to add tests that cover your changes.

Make sure you've run and fixed any issues with these commands:

- `cargo fmt --all -- --check` to check standard code formatting (`cargo
fmt --all` applies these changes)
- `cargo clippy --workspace -- -D warnings -D clippy::unwrap_used -A
clippy::needless_collect` to check that you're using the standard code
style
- `cargo test --workspace` to check that all tests pass

> **Note**
> from `nushell` you can also use the `toolkit` as follows
> ```bash
> use toolkit.nu # or use an `env_change` hook to activate it
automatically
> toolkit check pr
> ```

# After Submitting

If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.

---------

Co-authored-by: Stefan Holderbach <sholderbach@users.noreply.github.com>
This commit is contained in:
Kelvin Samuel 2023-03-30 16:35:35 +01:00 committed by GitHub
parent e9c17daecd
commit bc6948dc89
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 121 additions and 17 deletions

View file

@ -145,6 +145,22 @@ impl Command for SubCommand {
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,
}),
},
]
}
}
@ -197,6 +213,8 @@ fn convert_str_from_unit_to_unit(
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),
@ -211,6 +229,8 @@ fn convert_str_from_unit_to_unit(
("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),
@ -221,8 +241,40 @@ fn convert_str_from_unit_to_unit(
("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),
@ -235,6 +287,8 @@ fn convert_str_from_unit_to_unit(
("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),
@ -247,6 +301,8 @@ fn convert_str_from_unit_to_unit(
("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),
@ -259,6 +315,8 @@ fn convert_str_from_unit_to_unit(
("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),
@ -271,6 +329,8 @@ fn convert_str_from_unit_to_unit(
("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),
@ -283,6 +343,8 @@ fn convert_str_from_unit_to_unit(
("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),
@ -295,6 +357,8 @@ fn convert_str_from_unit_to_unit(
("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),
@ -307,6 +371,8 @@ fn convert_str_from_unit_to_unit(
("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),
@ -324,7 +390,7 @@ fn convert_str_from_unit_to_unit(
dst_span: span,
src_span: value_span,
help: Some(
"supported units are ns, us, ms, sec, min, hr, day, wk, month, yr and dec"
"supported units are ns, us/µs, ms, sec, min, hr, day, wk, month, yr, and dec"
.to_string(),
),
}),
@ -357,7 +423,8 @@ fn string_to_duration(s: &str, span: Span, value_span: Span) -> Result<i64, Shel
dst_span: span,
src_span: value_span,
help: Some(
"supported units are ns, us, ms, sec, min, hr, day, wk, month, yr and dec".to_string(),
"supported units are ns, us/µs, ms, sec, min, hr, day, wk, month, yr, and dec"
.to_string(),
),
})
}
@ -393,7 +460,8 @@ fn string_to_unit_duration(
dst_span: span,
src_span: value_span,
help: Some(
"supported units are ns, us, ms, sec, min, hr, day, wk, month, yr and dec".to_string(),
"supported units are ns, us/µs, ms, sec, min, hr, day, wk, month, yr, and dec"
.to_string(),
),
})
}
@ -536,6 +604,34 @@ mod test {
assert_eq!(actual, expected);
}
#[test]
fn turns_micro_sign_s_to_duration() {
let span = Span::new(0, 2);
let word = Value::test_string("4\u{00B5}s");
let expected = Value::Duration {
val: 4 * 1000,
span,
};
let convert_duration = None;
let actual = action(&word, &convert_duration, 2, span);
assert_eq!(actual, expected);
}
#[test]
fn turns_mu_s_to_duration() {
let span = Span::new(0, 2);
let word = Value::test_string("4\u{03BC}s");
let expected = Value::Duration {
val: 4 * 1000,
span,
};
let convert_duration = None;
let actual = action(&word, &convert_duration, 2, span);
assert_eq!(actual, expected);
}
#[test]
fn turns_ms_to_duration() {
let span = Span::new(0, 2);

View file

@ -2604,24 +2604,32 @@ pub fn parse_duration_bytes(num_with_unit_bytes: &[u8], span: Span) -> Option<Ex
}
let num_with_unit = String::from_utf8_lossy(num_with_unit_bytes).to_string();
let uppercase_num_with_unit = num_with_unit.to_uppercase();
let unit_groups = [
(Unit::Nanosecond, "NS", None),
(Unit::Microsecond, "US", Some((Unit::Nanosecond, 1000))),
(Unit::Millisecond, "MS", Some((Unit::Microsecond, 1000))),
(Unit::Second, "SEC", Some((Unit::Millisecond, 1000))),
(Unit::Minute, "MIN", Some((Unit::Second, 60))),
(Unit::Hour, "HR", Some((Unit::Minute, 60))),
(Unit::Day, "DAY", Some((Unit::Minute, 1440))),
(Unit::Week, "WK", Some((Unit::Day, 7))),
(Unit::Nanosecond, "ns", None),
(Unit::Microsecond, "us", Some((Unit::Nanosecond, 1000))),
(
// µ Micro Sign
Unit::Microsecond,
"\u{00B5}s",
Some((Unit::Nanosecond, 1000)),
),
(
// μ Greek small letter Mu
Unit::Microsecond,
"\u{03BC}s",
Some((Unit::Nanosecond, 1000)),
),
(Unit::Millisecond, "ms", Some((Unit::Microsecond, 1000))),
(Unit::Second, "sec", Some((Unit::Millisecond, 1000))),
(Unit::Minute, "min", Some((Unit::Second, 60))),
(Unit::Hour, "hr", Some((Unit::Minute, 60))),
(Unit::Day, "day", Some((Unit::Minute, 1440))),
(Unit::Week, "wk", Some((Unit::Day, 7))),
];
if let Some(unit) = unit_groups
.iter()
.find(|&x| uppercase_num_with_unit.ends_with(x.1))
{
if let Some(unit) = unit_groups.iter().find(|&x| num_with_unit.ends_with(x.1)) {
let mut lhs = num_with_unit;
for _ in 0..unit.1.len() {
for _ in 0..unit.1.chars().count() {
lhs.pop();
}