mirror of
https://github.com/nushell/nushell.git
synced 2024-11-25 18:03:51 +01:00
changed the way durations and filesizes are parsed (#6640)
This commit is contained in:
parent
6aa8a0073b
commit
6486364610
@ -402,6 +402,18 @@ fn duration_decimal_math_with_all_units() {
|
|||||||
assert_eq!(actual.out, "1wk 3day 8hr 10min 16sec 121ms 11µs 12ns");
|
assert_eq!(actual.out, "1wk 3day 8hr 10min 16sec 121ms 11µs 12ns");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn duration_decimal_dans_test() {
|
||||||
|
let actual = nu!(
|
||||||
|
cwd: "tests/fixtures/formats", pipeline(
|
||||||
|
r#"
|
||||||
|
3.14sec
|
||||||
|
"#
|
||||||
|
));
|
||||||
|
|
||||||
|
assert_eq!(actual.out, "3sec 140ms");
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn duration_math_with_negative() {
|
fn duration_math_with_negative() {
|
||||||
let actual = nu!(
|
let actual = nu!(
|
||||||
|
@ -1538,20 +1538,36 @@ fn compute(size: i64, unit: Unit, span: Span) -> Value {
|
|||||||
val: size * 1000 * 1000 * 1000,
|
val: size * 1000 * 1000 * 1000,
|
||||||
span,
|
span,
|
||||||
},
|
},
|
||||||
Unit::Minute => Value::Duration {
|
Unit::Minute => match size.checked_mul(1000 * 1000 * 1000 * 60) {
|
||||||
val: size * 1000 * 1000 * 1000 * 60,
|
Some(val) => Value::Duration { val, span },
|
||||||
span,
|
None => Value::Error {
|
||||||
|
error: ShellError::GenericError(
|
||||||
|
"duration too large".into(),
|
||||||
|
"duration too large".into(),
|
||||||
|
Some(span),
|
||||||
|
None,
|
||||||
|
Vec::new(),
|
||||||
|
),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
Unit::Hour => Value::Duration {
|
Unit::Hour => match size.checked_mul(1000 * 1000 * 1000 * 60 * 60) {
|
||||||
val: size * 1000 * 1000 * 1000 * 60 * 60,
|
Some(val) => Value::Duration { val, span },
|
||||||
span,
|
None => Value::Error {
|
||||||
|
error: ShellError::GenericError(
|
||||||
|
"duration too large".into(),
|
||||||
|
"duration too large".into(),
|
||||||
|
Some(span),
|
||||||
|
None,
|
||||||
|
Vec::new(),
|
||||||
|
),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
Unit::Day => match size.checked_mul(1000 * 1000 * 1000 * 60 * 60 * 24) {
|
Unit::Day => match size.checked_mul(1000 * 1000 * 1000 * 60 * 60 * 24) {
|
||||||
Some(val) => Value::Duration { val, span },
|
Some(val) => Value::Duration { val, span },
|
||||||
None => Value::Error {
|
None => Value::Error {
|
||||||
error: ShellError::GenericError(
|
error: ShellError::GenericError(
|
||||||
"day duration too large".into(),
|
"duration too large".into(),
|
||||||
"day duration too large".into(),
|
"duration too large".into(),
|
||||||
Some(span),
|
Some(span),
|
||||||
None,
|
None,
|
||||||
Vec::new(),
|
Vec::new(),
|
||||||
@ -1562,8 +1578,8 @@ fn compute(size: i64, unit: Unit, span: Span) -> Value {
|
|||||||
Some(val) => Value::Duration { val, span },
|
Some(val) => Value::Duration { val, span },
|
||||||
None => Value::Error {
|
None => Value::Error {
|
||||||
error: ShellError::GenericError(
|
error: ShellError::GenericError(
|
||||||
"week duration too large".into(),
|
"duration too large".into(),
|
||||||
"week duration too large".into(),
|
"duration too large".into(),
|
||||||
Some(span),
|
Some(span),
|
||||||
None,
|
None,
|
||||||
Vec::new(),
|
Vec::new(),
|
||||||
|
@ -2175,23 +2175,50 @@ pub fn parse_duration(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_duration_bytes(bytes: &[u8], span: Span) -> Option<Expression> {
|
// Borrowed from libm at https://github.com/rust-lang/libm/blob/master/src/math/modf.rs
|
||||||
fn parse_decimal_str_to_number(decimal: &str) -> Option<i64> {
|
pub fn modf(x: f64) -> (f64, f64) {
|
||||||
let string_to_parse = format!("0.{}", decimal);
|
let rv2: f64;
|
||||||
if let Ok(x) = string_to_parse.parse::<f64>() {
|
let mut u = x.to_bits();
|
||||||
return Some((1_f64 / x) as i64);
|
let e = ((u >> 52 & 0x7ff) as i32) - 0x3ff;
|
||||||
|
|
||||||
|
/* no fractional part */
|
||||||
|
if e >= 52 {
|
||||||
|
rv2 = x;
|
||||||
|
if e == 0x400 && (u << 12) != 0 {
|
||||||
|
/* nan */
|
||||||
|
return (x, rv2);
|
||||||
}
|
}
|
||||||
None
|
u &= 1 << 63;
|
||||||
|
return (f64::from_bits(u), rv2);
|
||||||
}
|
}
|
||||||
|
|
||||||
if bytes.is_empty() || (!bytes[0].is_ascii_digit() && bytes[0] != b'-') {
|
/* no integral part*/
|
||||||
|
if e < 0 {
|
||||||
|
u &= 1 << 63;
|
||||||
|
rv2 = f64::from_bits(u);
|
||||||
|
return (x, rv2);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mask = ((!0) >> 12) >> e;
|
||||||
|
if (u & mask) == 0 {
|
||||||
|
rv2 = x;
|
||||||
|
u &= 1 << 63;
|
||||||
|
return (f64::from_bits(u), rv2);
|
||||||
|
}
|
||||||
|
u &= !mask;
|
||||||
|
rv2 = f64::from_bits(u);
|
||||||
|
(x - rv2, rv2)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse_duration_bytes(num_with_unit_bytes: &[u8], span: Span) -> Option<Expression> {
|
||||||
|
if num_with_unit_bytes.is_empty()
|
||||||
|
|| (!num_with_unit_bytes[0].is_ascii_digit() && num_with_unit_bytes[0] != b'-')
|
||||||
|
{
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
let token = String::from_utf8_lossy(bytes).to_string();
|
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 upper = token.to_uppercase();
|
|
||||||
|
|
||||||
let unit_groups = [
|
let unit_groups = [
|
||||||
(Unit::Nanosecond, "NS", None),
|
(Unit::Nanosecond, "NS", None),
|
||||||
(Unit::Microsecond, "US", Some((Unit::Nanosecond, 1000))),
|
(Unit::Microsecond, "US", Some((Unit::Nanosecond, 1000))),
|
||||||
@ -2202,34 +2229,33 @@ pub fn parse_duration_bytes(bytes: &[u8], span: Span) -> Option<Expression> {
|
|||||||
(Unit::Day, "DAY", Some((Unit::Minute, 1440))),
|
(Unit::Day, "DAY", Some((Unit::Minute, 1440))),
|
||||||
(Unit::Week, "WK", Some((Unit::Day, 7))),
|
(Unit::Week, "WK", Some((Unit::Day, 7))),
|
||||||
];
|
];
|
||||||
if let Some(unit) = unit_groups.iter().find(|&x| upper.ends_with(x.1)) {
|
|
||||||
let mut lhs = token;
|
if let Some(unit) = unit_groups
|
||||||
|
.iter()
|
||||||
|
.find(|&x| uppercase_num_with_unit.ends_with(x.1))
|
||||||
|
{
|
||||||
|
let mut lhs = num_with_unit;
|
||||||
for _ in 0..unit.1.len() {
|
for _ in 0..unit.1.len() {
|
||||||
lhs.pop();
|
lhs.pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
let input: Vec<&str> = lhs.split('.').collect();
|
let (decimal_part, number_part) = modf(match lhs.parse::<f64>() {
|
||||||
let (value, unit_to_use) = match &input[..] {
|
Ok(x) => x,
|
||||||
[number_str] => (number_str.parse::<i64>().ok(), unit.0),
|
Err(_) => return None,
|
||||||
[number_str, decimal_part_str] => match unit.2 {
|
});
|
||||||
Some(unit_to_convert_to) => match (
|
|
||||||
number_str.parse::<i64>(),
|
let (num, unit_to_use) = match unit.2 {
|
||||||
parse_decimal_str_to_number(decimal_part_str),
|
Some(unit_to_convert_to) => (
|
||||||
) {
|
Some(
|
||||||
(Ok(number), Some(decimal_part)) => (
|
((number_part * unit_to_convert_to.1 as f64)
|
||||||
Some(
|
+ (decimal_part * unit_to_convert_to.1 as f64)) as i64,
|
||||||
(number * unit_to_convert_to.1) + (unit_to_convert_to.1 / decimal_part),
|
),
|
||||||
),
|
unit_to_convert_to.0,
|
||||||
unit_to_convert_to.0,
|
),
|
||||||
),
|
None => (Some(number_part as i64), unit.0),
|
||||||
_ => (None, unit.0),
|
|
||||||
},
|
|
||||||
None => (None, unit.0),
|
|
||||||
},
|
|
||||||
_ => (None, unit.0),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(x) = value {
|
if let Some(x) = num {
|
||||||
trace!("-- found {} {:?}", x, unit_to_use);
|
trace!("-- found {} {:?}", x, unit_to_use);
|
||||||
|
|
||||||
let lhs_span = Span::new(span.start, span.start + lhs.len());
|
let lhs_span = Span::new(span.start, span.start + lhs.len());
|
||||||
@ -2262,33 +2288,32 @@ pub fn parse_filesize(
|
|||||||
working_set: &StateWorkingSet,
|
working_set: &StateWorkingSet,
|
||||||
span: Span,
|
span: Span,
|
||||||
) -> (Expression, Option<ParseError>) {
|
) -> (Expression, Option<ParseError>) {
|
||||||
trace!("parsing: duration");
|
trace!("parsing: filesize");
|
||||||
|
|
||||||
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 bytes = working_set.get_span_contents(span);
|
let bytes = working_set.get_span_contents(span);
|
||||||
|
|
||||||
if bytes.is_empty() || (!bytes[0].is_ascii_digit() && bytes[0] != b'-') {
|
match parse_filesize_bytes(bytes, span) {
|
||||||
return (
|
Some(expression) => (expression, None),
|
||||||
|
None => (
|
||||||
garbage(span),
|
garbage(span),
|
||||||
Some(ParseError::Mismatch(
|
Some(ParseError::Mismatch(
|
||||||
"filesize".into(),
|
"filesize".into(),
|
||||||
"non-filesize unit".into(),
|
"non-filesize unit".into(),
|
||||||
span,
|
span,
|
||||||
)),
|
)),
|
||||||
);
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse_filesize_bytes(num_with_unit_bytes: &[u8], span: Span) -> Option<Expression> {
|
||||||
|
if num_with_unit_bytes.is_empty()
|
||||||
|
|| (!num_with_unit_bytes[0].is_ascii_digit() && num_with_unit_bytes[0] != b'-')
|
||||||
|
{
|
||||||
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
let token = String::from_utf8_lossy(bytes).to_string();
|
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 upper = token.to_uppercase();
|
|
||||||
|
|
||||||
let unit_groups = [
|
let unit_groups = [
|
||||||
(Unit::Kilobyte, "KB", Some((Unit::Byte, 1000))),
|
(Unit::Kilobyte, "KB", Some((Unit::Byte, 1000))),
|
||||||
(Unit::Megabyte, "MB", Some((Unit::Kilobyte, 1000))),
|
(Unit::Megabyte, "MB", Some((Unit::Kilobyte, 1000))),
|
||||||
@ -2306,69 +2331,58 @@ pub fn parse_filesize(
|
|||||||
(Unit::Zebibyte, "ZIB", Some((Unit::Exbibyte, 1024))),
|
(Unit::Zebibyte, "ZIB", Some((Unit::Exbibyte, 1024))),
|
||||||
(Unit::Byte, "B", None),
|
(Unit::Byte, "B", None),
|
||||||
];
|
];
|
||||||
if let Some(unit) = unit_groups.iter().find(|&x| upper.ends_with(x.1)) {
|
|
||||||
let mut lhs = token;
|
if let Some(unit) = unit_groups
|
||||||
|
.iter()
|
||||||
|
.find(|&x| uppercase_num_with_unit.ends_with(x.1))
|
||||||
|
{
|
||||||
|
let mut lhs = num_with_unit;
|
||||||
for _ in 0..unit.1.len() {
|
for _ in 0..unit.1.len() {
|
||||||
lhs.pop();
|
lhs.pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
let input: Vec<&str> = lhs.split('.').collect();
|
let (decimal_part, number_part) = modf(match lhs.parse::<f64>() {
|
||||||
let (value, unit_to_use) = match &input[..] {
|
Ok(x) => x,
|
||||||
[number_str] => (number_str.parse::<i64>().ok(), unit.0),
|
Err(_) => return None,
|
||||||
[number_str, decimal_part_str] => match unit.2 {
|
});
|
||||||
Some(unit_to_convert_to) => match (
|
|
||||||
number_str.parse::<i64>(),
|
let (num, unit_to_use) = match unit.2 {
|
||||||
parse_decimal_str_to_number(decimal_part_str),
|
Some(unit_to_convert_to) => (
|
||||||
) {
|
Some(
|
||||||
(Ok(number), Some(decimal_part)) => (
|
((number_part * unit_to_convert_to.1 as f64)
|
||||||
Some(
|
+ (decimal_part * unit_to_convert_to.1 as f64)) as i64,
|
||||||
(number * unit_to_convert_to.1) + (unit_to_convert_to.1 / decimal_part),
|
),
|
||||||
),
|
unit_to_convert_to.0,
|
||||||
unit_to_convert_to.0,
|
),
|
||||||
),
|
None => (Some(number_part as i64), unit.0),
|
||||||
_ => (None, unit.0),
|
|
||||||
},
|
|
||||||
None => (None, unit.0),
|
|
||||||
},
|
|
||||||
_ => (None, unit.0),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(x) = value {
|
if let Some(x) = num {
|
||||||
trace!("-- found {} {:?}", x, unit_to_use);
|
trace!("-- found {} {:?}", x, unit_to_use);
|
||||||
|
|
||||||
let lhs_span = Span::new(span.start, span.start + lhs.len());
|
let lhs_span = Span::new(span.start, span.start + lhs.len());
|
||||||
let unit_span = Span::new(span.start + lhs.len(), span.end);
|
let unit_span = Span::new(span.start + lhs.len(), span.end);
|
||||||
return (
|
return Some(Expression {
|
||||||
Expression {
|
expr: Expr::ValueWithUnit(
|
||||||
expr: Expr::ValueWithUnit(
|
Box::new(Expression {
|
||||||
Box::new(Expression {
|
expr: Expr::Int(x),
|
||||||
expr: Expr::Int(x),
|
span: lhs_span,
|
||||||
span: lhs_span,
|
ty: Type::Number,
|
||||||
ty: Type::Number,
|
custom_completion: None,
|
||||||
custom_completion: None,
|
}),
|
||||||
}),
|
Spanned {
|
||||||
Spanned {
|
item: unit_to_use,
|
||||||
item: unit_to_use,
|
span: unit_span,
|
||||||
span: unit_span,
|
},
|
||||||
},
|
),
|
||||||
),
|
span,
|
||||||
span,
|
ty: Type::Filesize,
|
||||||
ty: Type::Filesize,
|
custom_completion: None,
|
||||||
custom_completion: None,
|
});
|
||||||
},
|
|
||||||
None,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
(
|
None
|
||||||
garbage(span),
|
|
||||||
Some(ParseError::Mismatch(
|
|
||||||
"filesize".into(),
|
|
||||||
"non-filesize unit".into(),
|
|
||||||
span,
|
|
||||||
)),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_glob_pattern(
|
pub fn parse_glob_pattern(
|
||||||
|
Loading…
Reference in New Issue
Block a user