Add Filesize type (#14369)

# Description
Adds a new `Filesize` type so that `FromValue` can be used to convert a
`Value::Filesize` to a `Filesize`. Currently, to extract a filesize from
a `Value` using `FromValue`, you have to extract an `i64` which coerces
`Value::Int`, `Value::Duration`, and `Value::Filesize` to an `i64`.

Having a separate type also allows us to enforce checked math to catch
overflows. Similarly, it allows us to specify other trait
implementations like `Display` in a common place.

# User-Facing Changes
Multiplication with filesizes now error on overflow. Should not be a
breaking change for plugins (i.e., serialization) since `Filesize` is
marked with `serde(transparent)`.

# Tests + Formatting
Updated some tests.
This commit is contained in:
Ian Manske
2024-11-29 13:24:17 -08:00
committed by GitHub
parent acca56f77c
commit 7f61cbbfd6
26 changed files with 827 additions and 322 deletions

View File

@ -1,7 +1,7 @@
use nu_protocol::{
ast::{Expr, Expression, ListItem, RecordItem},
engine::{EngineState, StateWorkingSet},
Range, Record, ShellError, Span, Type, Unit, Value,
Filesize, IntoValue, Range, Record, ShellError, Span, Type, Unit, Value,
};
use std::sync::Arc;
@ -9,7 +9,7 @@ use std::sync::Arc;
///
// WARNING: please leave the following two trailing spaces, they matter for the documentation
// formatting
/// > **Note**
/// > **Note**
/// > [`Span`] can be passed to [`from_nuon`] if there is context available to the caller, e.g. when
/// > using this function in a command implementation such as
/// > [`from nuon`](https://www.nushell.sh/commands/docs/from_nuon.html).
@ -407,32 +407,15 @@ fn convert_to_value(
};
match value.unit.item {
Unit::Byte => Ok(Value::filesize(size, span)),
Unit::Kilobyte => Ok(Value::filesize(size * 1000, span)),
Unit::Megabyte => Ok(Value::filesize(size * 1000 * 1000, span)),
Unit::Gigabyte => Ok(Value::filesize(size * 1000 * 1000 * 1000, span)),
Unit::Terabyte => Ok(Value::filesize(size * 1000 * 1000 * 1000 * 1000, span)),
Unit::Petabyte => Ok(Value::filesize(
size * 1000 * 1000 * 1000 * 1000 * 1000,
span,
)),
Unit::Exabyte => Ok(Value::filesize(
size * 1000 * 1000 * 1000 * 1000 * 1000 * 1000,
span,
)),
Unit::Kibibyte => Ok(Value::filesize(size * 1024, span)),
Unit::Mebibyte => Ok(Value::filesize(size * 1024 * 1024, span)),
Unit::Gibibyte => Ok(Value::filesize(size * 1024 * 1024 * 1024, span)),
Unit::Tebibyte => Ok(Value::filesize(size * 1024 * 1024 * 1024 * 1024, span)),
Unit::Pebibyte => Ok(Value::filesize(
size * 1024 * 1024 * 1024 * 1024 * 1024,
span,
)),
Unit::Exbibyte => Ok(Value::filesize(
size * 1024 * 1024 * 1024 * 1024 * 1024 * 1024,
span,
)),
Unit::Filesize(unit) => match Filesize::from_unit(size, unit) {
Some(val) => Ok(val.into_value(span)),
None => Err(ShellError::OutsideSpannedLabeledError {
src: original_text.into(),
error: "filesize too large".into(),
msg: "filesize too large".into(),
span: expr.span,
}),
},
Unit::Nanosecond => Ok(Value::duration(size, span)),
Unit::Microsecond => Ok(Value::duration(size * 1000, span)),

View File

@ -155,7 +155,7 @@ mod tests {
#[test]
fn filesize() {
nuon_end_to_end("1024b", Some(Value::test_filesize(1024)));
assert_eq!(from_nuon("1kib", None).unwrap(), Value::test_filesize(1024),);
assert_eq!(from_nuon("1kib", None).unwrap(), Value::test_filesize(1024));
}
#[test]

View File

@ -39,7 +39,7 @@ pub enum ToStyle {
///
// WARNING: please leave the following two trailing spaces, they matter for the documentation
// formatting
/// > **Note**
/// > **Note**
/// > a [`Span`] can be passed to [`to_nuon`] if there is context available to the caller, e.g. when
/// > using this function in a command implementation such as [`to nuon`](https://www.nushell.sh/commands/docs/to_nuon.html).
///
@ -110,7 +110,7 @@ fn value_to_string(
// Propagate existing errors
Value::Error { error, .. } => Err(*error.clone()),
// FIXME: make filesizes use the shortest lossless representation.
Value::Filesize { val, .. } => Ok(format!("{}b", *val)),
Value::Filesize { val, .. } => Ok(format!("{}b", val.get())),
Value::Float { val, .. } => {
// This serialises these as 'nan', 'inf' and '-inf', respectively.
if &val.round() == val && val.is_finite() {