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,5 +1,5 @@
use nu_engine::command_prelude::*;
use nu_protocol::format_filesize_from_conf;
use rand::{thread_rng, RngCore};
#[derive(Clone)]
@ -37,7 +37,27 @@ impl Command for SubCommand {
call: &Call,
_input: PipelineData,
) -> Result<PipelineData, ShellError> {
let length = call.req(engine_state, stack, 0)?;
let length_val = call.req(engine_state, stack, 0)?;
let length = match length_val {
Value::Int { val, .. } => usize::try_from(val).map_err(|_| ShellError::InvalidValue {
valid: "a non-negative int or filesize".into(),
actual: val.to_string(),
span: length_val.span(),
}),
Value::Filesize { val, .. } => {
usize::try_from(val).map_err(|_| ShellError::InvalidValue {
valid: "a non-negative int or filesize".into(),
actual: format_filesize_from_conf(val, engine_state.get_config()),
span: length_val.span(),
})
}
val => Err(ShellError::RuntimeTypeMismatch {
expected: Type::custom("int or filesize"),
actual: val.get_type(),
span: val.span(),
}),
}?;
let mut rng = thread_rng();
let mut out = vec![0u8; length];

View File

@ -1,5 +1,5 @@
use nu_engine::command_prelude::*;
use nu_protocol::format_filesize_from_conf;
use rand::{
distributions::{Alphanumeric, Distribution},
thread_rng,
@ -73,14 +73,36 @@ fn chars(
call: &Call,
) -> Result<PipelineData, ShellError> {
let span = call.head;
let length: Option<usize> = call.get_flag(engine_state, stack, "length")?;
let length: Option<Value> = call.get_flag(engine_state, stack, "length")?;
let length = if let Some(length_val) = length {
match length_val {
Value::Int { val, .. } => usize::try_from(val).map_err(|_| ShellError::InvalidValue {
valid: "a non-negative int or filesize".into(),
actual: val.to_string(),
span: length_val.span(),
}),
Value::Filesize { val, .. } => {
usize::try_from(val).map_err(|_| ShellError::InvalidValue {
valid: "a non-negative int or filesize".into(),
actual: format_filesize_from_conf(val, engine_state.get_config()),
span: length_val.span(),
})
}
val => Err(ShellError::RuntimeTypeMismatch {
expected: Type::custom("int or filesize"),
actual: val.get_type(),
span: val.span(),
}),
}?
} else {
DEFAULT_CHARS_LENGTH
};
let chars_length = length.unwrap_or(DEFAULT_CHARS_LENGTH);
let mut rng = thread_rng();
let random_string = Alphanumeric
.sample_iter(&mut rng)
.take(chars_length)
.take(length)
.map(char::from)
.collect::<String>();