Split unit into duration and filesize (#3453)

This commit is contained in:
JT
2021-05-21 13:21:46 +12:00
committed by GitHub
parent 4fdbf30308
commit 28388b4e3a
6 changed files with 126 additions and 38 deletions

View File

@ -331,8 +331,76 @@ fn parse_operator(lite_arg: &Spanned<String>) -> (SpannedExpression, Option<Pars
)
}
/// Parse a duration type, eg '10day'
fn parse_duration(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 = [
(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))),
];
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();
}
let input: Vec<&str> = lhs.split('.').collect();
let (value, unit_to_use) = match &input[..] {
[number_str] => (number_str.parse::<i64>().ok(), unit.0),
[number_str, decimal_part_str] => match unit.2 {
Some(unit_to_convert_to) => match (
number_str.parse::<i64>(),
parse_decimal_str_to_number(decimal_part_str),
) {
(Ok(number), Some(decimal_part)) => (
Some(
(number * unit_to_convert_to.1) + (unit_to_convert_to.1 / decimal_part),
),
unit_to_convert_to.0,
),
_ => (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,
);
}
}
(
garbage(lite_arg.span),
Some(ParseError::mismatch("duration", lite_arg.clone())),
)
}
/// Parse a unit type, eg '10kb'
fn parse_unit(lite_arg: &Spanned<String>) -> (SpannedExpression, Option<ParseError>) {
fn parse_filesize(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>() {
@ -352,14 +420,6 @@ fn parse_unit(lite_arg: &Spanned<String>) -> (SpannedExpression, Option<ParseErr
(Unit::Tebibyte, "TIB", Some((Unit::Gibibyte, 1024))),
(Unit::Pebibyte, "PIB", Some((Unit::Tebibyte, 1024))),
(Unit::Byte, "B", None),
(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))),
];
if let Some(unit) = unit_groups
.iter()
@ -467,7 +527,11 @@ fn parse_dollar_expr(
scope: &dyn ParserScope,
) -> (SpannedExpression, Option<ParseError>) {
trace!("Parsing dollar expression: {:?}", lite_arg.item);
if lite_arg.item.starts_with("$\"") && lite_arg.item.len() > 1 && lite_arg.item.ends_with('"') {
if (lite_arg.item.starts_with("$\"") && lite_arg.item.len() > 1 && lite_arg.item.ends_with('"'))
|| (lite_arg.item.starts_with("$'")
&& lite_arg.item.len() > 1
&& lite_arg.item.ends_with('\''))
{
// This is an interpolated string
parse_interpolated_string(&lite_arg, scope)
} else if let (expr, None) = parse_range(lite_arg, scope) {
@ -844,7 +908,8 @@ fn parse_arg(
SyntaxShape::Range => parse_range(&lite_arg, scope),
SyntaxShape::Operator => parse_operator(&lite_arg),
SyntaxShape::Unit => parse_unit(&lite_arg),
SyntaxShape::Filesize => parse_filesize(&lite_arg),
SyntaxShape::Duration => parse_duration(&lite_arg),
SyntaxShape::FilePath => {
let trimmed = trim_quotes(&lite_arg.item);
let expanded = expand_path(&trimmed).to_string();
@ -861,7 +926,8 @@ fn parse_arg(
SyntaxShape::Int,
SyntaxShape::Number,
SyntaxShape::Range,
SyntaxShape::Unit,
SyntaxShape::Filesize,
SyntaxShape::Duration,
SyntaxShape::Block,
SyntaxShape::Table,
SyntaxShape::String,
@ -2164,7 +2230,7 @@ fn unit_parse_byte_units() {
let input_len = case.string.len();
let value_len = case.value.to_string().len();
let input = case.string.clone().spanned(Span::new(0, input_len));
let result = parse_unit(&input);
let result = parse_filesize(&input);
assert_eq!(result.1, None);
assert_eq!(
result.0.expr,
@ -2252,7 +2318,7 @@ fn unit_parse_byte_units_decimal() {
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);
let result = parse_filesize(&input);
assert_eq!(result.1, None);
assert_eq!(
result.0.expr,

View File

@ -166,7 +166,8 @@ pub fn parse_type_token(type_: &Token) -> (SyntaxShape, Option<ParseError>) {
"string" => (SyntaxShape::String, None),
"path" => (SyntaxShape::FilePath, None),
"table" => (SyntaxShape::Table, None),
"unit" => (SyntaxShape::Unit, None),
"duration" => (SyntaxShape::Duration, None),
"filesize" => (SyntaxShape::Filesize, None),
"number" => (SyntaxShape::Number, None),
"pattern" => (SyntaxShape::GlobPattern, None),
"range" => (SyntaxShape::Range, None),