mirror of
https://github.com/nushell/nushell
synced 2024-12-27 21:43:09 +00:00
Parse decimal units (#3243)
* parse decimal units * linting * stop clippy complaining * Added tests to parsing decimals * Fixed bug * Fixed testing and add more
This commit is contained in:
parent
e737222a5d
commit
2146ede15d
2 changed files with 176 additions and 41 deletions
|
@ -187,6 +187,18 @@ fn duration_math() {
|
||||||
assert_eq!(actual.out, "8day");
|
assert_eq!(actual.out, "8day");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn duration_decimal_math() {
|
||||||
|
let actual = nu!(
|
||||||
|
cwd: "tests/fixtures/formats", pipeline(
|
||||||
|
r#"
|
||||||
|
= 0.5mon + 1day
|
||||||
|
"#
|
||||||
|
));
|
||||||
|
|
||||||
|
assert_eq!(actual.out, "16day");
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn duration_math_with_nanoseconds() {
|
fn duration_math_with_nanoseconds() {
|
||||||
let actual = nu!(
|
let actual = nu!(
|
||||||
|
@ -199,6 +211,18 @@ fn duration_math_with_nanoseconds() {
|
||||||
assert_eq!(actual.out, "7day 10ns");
|
assert_eq!(actual.out, "7day 10ns");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn duration_decimal_math_with_nanoseconds() {
|
||||||
|
let actual = nu!(
|
||||||
|
cwd: "tests/fixtures/formats", pipeline(
|
||||||
|
r#"
|
||||||
|
= 1.5wk + 10ns
|
||||||
|
"#
|
||||||
|
));
|
||||||
|
|
||||||
|
assert_eq!(actual.out, "10day 10ns");
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn duration_math_with_negative() {
|
fn duration_math_with_negative() {
|
||||||
let actual = nu!(
|
let actual = nu!(
|
||||||
|
|
|
@ -325,51 +325,74 @@ fn parse_operator(lite_arg: &Spanned<String>) -> (SpannedExpression, Option<Pars
|
||||||
|
|
||||||
/// Parse a unit type, eg '10kb'
|
/// Parse a unit type, eg '10kb'
|
||||||
fn parse_unit(lite_arg: &Spanned<String>) -> (SpannedExpression, Option<ParseError>) {
|
fn parse_unit(lite_arg: &Spanned<String>) -> (SpannedExpression, Option<ParseError>) {
|
||||||
|
fn parse_decimal_str_to_number(decimal: &str) -> Option<i64> {
|
||||||
|
let string_to_parse = format!("0.{}", decimal);
|
||||||
|
if let Ok(x) = string_to_parse.parse::<f64>() {
|
||||||
|
return Some((1_f64 / x) as i64);
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
let unit_groups = [
|
let unit_groups = [
|
||||||
(Unit::Byte, vec!["b", "B"]),
|
(Unit::Kilobyte, "KB", Some((Unit::Byte, 1000))),
|
||||||
(Unit::Kilobyte, vec!["kb", "KB", "Kb", "kB"]),
|
(Unit::Megabyte, "MB", Some((Unit::Kilobyte, 1000))),
|
||||||
(Unit::Megabyte, vec!["mb", "MB", "Mb", "mB"]),
|
(Unit::Gigabyte, "GB", Some((Unit::Megabyte, 1000))),
|
||||||
(Unit::Gigabyte, vec!["gb", "GB", "Gb", "gB"]),
|
(Unit::Terabyte, "TB", Some((Unit::Gigabyte, 1000))),
|
||||||
(Unit::Terabyte, vec!["tb", "TB", "Tb", "tB"]),
|
(Unit::Petabyte, "PB", Some((Unit::Terabyte, 1000))),
|
||||||
(Unit::Petabyte, vec!["pb", "PB", "Pb", "pB"]),
|
(Unit::Kibibyte, "KIB", Some((Unit::Byte, 1024))),
|
||||||
(Unit::Kibibyte, vec!["KiB", "kib", "kiB", "Kib"]),
|
(Unit::Mebibyte, "MIB", Some((Unit::Kibibyte, 1024))),
|
||||||
(Unit::Mebibyte, vec!["MiB", "mib", "miB", "Mib"]),
|
(Unit::Gibibyte, "GIB", Some((Unit::Mebibyte, 1024))),
|
||||||
(Unit::Gibibyte, vec!["GiB", "gib", "giB", "Gib"]),
|
(Unit::Byte, "B", None),
|
||||||
(Unit::Nanosecond, vec!["ns"]),
|
(Unit::Nanosecond, "NS", None),
|
||||||
(Unit::Microsecond, vec!["us"]),
|
(Unit::Microsecond, "US", Some((Unit::Nanosecond, 1000))),
|
||||||
(Unit::Millisecond, vec!["ms"]),
|
(Unit::Millisecond, "MS", Some((Unit::Microsecond, 1000))),
|
||||||
(Unit::Second, vec!["sec"]),
|
(Unit::Second, "SEC", Some((Unit::Millisecond, 1000))),
|
||||||
(Unit::Minute, vec!["min"]),
|
(Unit::Minute, "MIN", Some((Unit::Second, 60))),
|
||||||
(Unit::Hour, vec!["hr"]),
|
(Unit::Hour, "HR", Some((Unit::Minute, 60))),
|
||||||
(Unit::Day, vec!["day"]),
|
(Unit::Day, "DAY", Some((Unit::Minute, 1440))),
|
||||||
(Unit::Week, vec!["wk"]),
|
(Unit::Week, "WK", Some((Unit::Day, 7))),
|
||||||
(Unit::Month, vec!["mon"]),
|
(Unit::Month, "MON", Some((Unit::Day, 30))),
|
||||||
(Unit::Year, vec!["yr"]),
|
(Unit::Year, "YR", Some((Unit::Day, 365))),
|
||||||
];
|
];
|
||||||
|
if let Some(unit) = unit_groups
|
||||||
|
.iter()
|
||||||
|
.find(|&x| lite_arg.to_uppercase().ends_with(x.1))
|
||||||
|
{
|
||||||
|
let mut lhs = lite_arg.item.clone();
|
||||||
|
for _ in 0..unit.1.len() {
|
||||||
|
lhs.pop();
|
||||||
|
}
|
||||||
|
|
||||||
for unit_group in unit_groups.iter() {
|
let input: Vec<&str> = lhs.split('.').collect();
|
||||||
for unit in unit_group.1.iter() {
|
let (value, unit_to_use) = match &input[..] {
|
||||||
if !lite_arg.item.ends_with(unit) {
|
[number_str] => (number_str.parse::<i64>().ok(), unit.0),
|
||||||
continue;
|
[number_str, decimal_part_str] => match unit.2 {
|
||||||
}
|
Some(unit_to_convert_to) => match (
|
||||||
let mut lhs = lite_arg.item.clone();
|
number_str.parse::<i64>(),
|
||||||
|
parse_decimal_str_to_number(decimal_part_str),
|
||||||
for _ in 0..unit.len() {
|
) {
|
||||||
lhs.pop();
|
(Ok(number), Some(decimal_part)) => (
|
||||||
}
|
Some(
|
||||||
|
(number * unit_to_convert_to.1) + (unit_to_convert_to.1 / decimal_part),
|
||||||
// these units are allowed to be signed
|
),
|
||||||
if let Ok(x) = lhs.parse::<i64>() {
|
unit_to_convert_to.0,
|
||||||
let lhs_span = Span::new(lite_arg.span.start(), lite_arg.span.start() + lhs.len());
|
|
||||||
let unit_span = Span::new(lite_arg.span.start() + lhs.len(), lite_arg.span.end());
|
|
||||||
return (
|
|
||||||
SpannedExpression::new(
|
|
||||||
Expression::unit(x.spanned(lhs_span), unit_group.0.spanned(unit_span)),
|
|
||||||
lite_arg.span,
|
|
||||||
),
|
),
|
||||||
None,
|
_ => (None, unit.0),
|
||||||
);
|
},
|
||||||
}
|
None => (None, unit.0),
|
||||||
|
},
|
||||||
|
_ => (None, unit.0),
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(x) = value {
|
||||||
|
let lhs_span = Span::new(lite_arg.span.start(), lite_arg.span.start() + lhs.len());
|
||||||
|
let unit_span = Span::new(lite_arg.span.start() + lhs.len(), lite_arg.span.end());
|
||||||
|
return (
|
||||||
|
SpannedExpression::new(
|
||||||
|
Expression::unit(x.spanned(lhs_span), unit_to_use.spanned(unit_span)),
|
||||||
|
lite_arg.span,
|
||||||
|
),
|
||||||
|
None,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2179,3 +2202,91 @@ fn unit_parse_byte_units() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn unit_parse_byte_units_decimal() {
|
||||||
|
struct TestCase {
|
||||||
|
string: String,
|
||||||
|
value: i64,
|
||||||
|
value_str: String,
|
||||||
|
unit: Unit,
|
||||||
|
}
|
||||||
|
|
||||||
|
let cases = [
|
||||||
|
TestCase {
|
||||||
|
string: String::from("0.25KB"),
|
||||||
|
value: 250,
|
||||||
|
value_str: String::from("0.25"),
|
||||||
|
unit: Unit::Byte,
|
||||||
|
},
|
||||||
|
TestCase {
|
||||||
|
string: String::from("2.5Mb"),
|
||||||
|
value: 2500,
|
||||||
|
value_str: String::from("2.5"),
|
||||||
|
unit: Unit::Kilobyte,
|
||||||
|
},
|
||||||
|
TestCase {
|
||||||
|
string: String::from("0.5Gb"),
|
||||||
|
value: 500,
|
||||||
|
value_str: String::from("0.5"),
|
||||||
|
unit: Unit::Megabyte,
|
||||||
|
},
|
||||||
|
TestCase {
|
||||||
|
string: String::from("811.5Gb"),
|
||||||
|
value: 811500,
|
||||||
|
value_str: String::from("811.5"),
|
||||||
|
unit: Unit::Megabyte,
|
||||||
|
},
|
||||||
|
TestCase {
|
||||||
|
string: String::from("11.5Tb"),
|
||||||
|
value: 11500,
|
||||||
|
value_str: String::from("11.5"),
|
||||||
|
unit: Unit::Gigabyte,
|
||||||
|
},
|
||||||
|
TestCase {
|
||||||
|
string: String::from("12.5Pb"),
|
||||||
|
value: 12500,
|
||||||
|
value_str: String::from("12.5"),
|
||||||
|
unit: Unit::Terabyte,
|
||||||
|
},
|
||||||
|
TestCase {
|
||||||
|
string: String::from("10.5kib"),
|
||||||
|
value: 10752,
|
||||||
|
value_str: String::from("10.5"),
|
||||||
|
unit: Unit::Byte,
|
||||||
|
},
|
||||||
|
TestCase {
|
||||||
|
string: String::from("0.5mib"),
|
||||||
|
value: 512,
|
||||||
|
value_str: String::from("0.5"),
|
||||||
|
unit: Unit::Kibibyte,
|
||||||
|
},
|
||||||
|
TestCase {
|
||||||
|
string: String::from("3.25gib"),
|
||||||
|
value: 3328,
|
||||||
|
value_str: String::from("3.25"),
|
||||||
|
unit: Unit::Mebibyte,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
for case in cases.iter() {
|
||||||
|
let input_len = case.string.len();
|
||||||
|
let value_len = case.value_str.to_string().len();
|
||||||
|
let input = case.string.clone().spanned(Span::new(0, input_len));
|
||||||
|
let result = parse_unit(&input);
|
||||||
|
assert_eq!(result.1, None);
|
||||||
|
assert_eq!(
|
||||||
|
result.0.expr,
|
||||||
|
Expression::unit(
|
||||||
|
Spanned {
|
||||||
|
span: Span::new(0, value_len),
|
||||||
|
item: case.value
|
||||||
|
},
|
||||||
|
Spanned {
|
||||||
|
span: Span::new(value_len, input_len),
|
||||||
|
item: case.unit
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue