diff --git a/crates/nu-command/src/random/binary.rs b/crates/nu-command/src/random/binary.rs index 539796b8c5..36d0179e3a 100644 --- a/crates/nu-command/src/random/binary.rs +++ b/crates/nu-command/src/random/binary.rs @@ -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 { - 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]; diff --git a/crates/nu-command/src/random/chars.rs b/crates/nu-command/src/random/chars.rs index d445a4aab8..e426fb5dbc 100644 --- a/crates/nu-command/src/random/chars.rs +++ b/crates/nu-command/src/random/chars.rs @@ -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 { let span = call.head; - let length: Option = call.get_flag(engine_state, stack, "length")?; + let length: Option = 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::(); diff --git a/crates/nu-command/tests/commands/reject.rs b/crates/nu-command/tests/commands/reject.rs index a2c052b3e1..15a0cba8a5 100644 --- a/crates/nu-command/tests/commands/reject.rs +++ b/crates/nu-command/tests/commands/reject.rs @@ -141,7 +141,7 @@ fn reject_rows_with_list_spread() { let actual = nu!("let arg = [2 0]; [[name type size];[Cargo.toml file 10mb] [Cargo.lock file 10mb] [src dir 100mb]] | reject ...$arg | to nuon"); assert_eq!( actual.out, - r#"[[name, type, size]; ["Cargo.lock", file, 10000000b]]"# + r#"[[name, type, size]; ["Cargo.lock", file, 10000000B]]"# ); } @@ -150,7 +150,7 @@ fn reject_mixed_with_list_spread() { let actual = nu!("let arg = [type 2]; [[name type size];[Cargp.toml file 10mb] [ Cargo.lock file 10mb] [src dir 100mb]] | reject ...$arg | to nuon"); assert_eq!( actual.out, - r#"[[name, size]; ["Cargp.toml", 10000000b], ["Cargo.lock", 10000000b]]"# + r#"[[name, size]; ["Cargp.toml", 10000000B], ["Cargo.lock", 10000000B]]"# ); } diff --git a/crates/nu-command/tests/format_conversions/nuon.rs b/crates/nu-command/tests/format_conversions/nuon.rs index 8262ee8dfc..6d96566521 100644 --- a/crates/nu-command/tests/format_conversions/nuon.rs +++ b/crates/nu-command/tests/format_conversions/nuon.rs @@ -5,9 +5,9 @@ fn to_nuon_correct_compaction() { let actual = nu!( cwd: "tests/fixtures/formats", pipeline( r#" - open appveyor.yml - | to nuon - | str length + open appveyor.yml + | to nuon + | str length | $in > 500 "# )); @@ -211,7 +211,7 @@ fn to_nuon_filesize() { "# )); - assert_eq!(actual.out, "1024b"); + assert_eq!(actual.out, "1024B"); } #[test] diff --git a/crates/nu-protocol/src/value/filesize.rs b/crates/nu-protocol/src/value/filesize.rs index 5baa9e6220..9e291db5bb 100644 --- a/crates/nu-protocol/src/value/filesize.rs +++ b/crates/nu-protocol/src/value/filesize.rs @@ -86,56 +86,6 @@ impl From for i64 { } } -impl TryFrom for Filesize { - type Error = >::Error; - - fn try_from(value: u64) -> Result { - value.try_into().map(Self) - } -} - -impl TryFrom for u64 { - type Error = >::Error; - - fn try_from(filesize: Filesize) -> Result { - filesize.0.try_into() - } -} - -/// The error type returned when a checked conversion from a floating point type fails. -#[derive(Debug, Copy, Clone, PartialEq, Eq, Error)] -pub struct TryFromFloatError(()); - -impl fmt::Display for TryFromFloatError { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(fmt, "out of range float type conversion attempted") - } -} - -impl TryFrom for Filesize { - type Error = TryFromFloatError; - - fn try_from(value: f64) -> Result { - if i64::MIN as f64 <= value && value <= i64::MAX as f64 { - Ok(Self(value as i64)) - } else { - Err(TryFromFloatError(())) - } - } -} - -impl TryFrom for Filesize { - type Error = TryFromFloatError; - - fn try_from(value: f32) -> Result { - if i64::MIN as f32 <= value && value <= i64::MAX as f32 { - Ok(Self(value as i64)) - } else { - Err(TryFromFloatError(())) - } - } -} - macro_rules! impl_from { ($($ty:ty),* $(,)?) => { $( @@ -160,6 +110,68 @@ macro_rules! impl_from { impl_from!(u8, i8, u16, i16, u32, i32); +macro_rules! impl_try_from { + ($($ty:ty),* $(,)?) => { + $( + impl TryFrom<$ty> for Filesize { + type Error = <$ty as TryInto>::Error; + + #[inline] + fn try_from(value: $ty) -> Result { + value.try_into().map(Self) + } + } + + impl TryFrom for $ty { + type Error = >::Error; + + #[inline] + fn try_from(filesize: Filesize) -> Result { + filesize.0.try_into() + } + } + )* + }; +} + +impl_try_from!(u64, usize, isize); + +/// The error type returned when a checked conversion from a floating point type fails. +#[derive(Debug, Copy, Clone, PartialEq, Eq, Error)] +pub struct TryFromFloatError(()); + +impl fmt::Display for TryFromFloatError { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(fmt, "out of range float type conversion attempted") + } +} + +impl TryFrom for Filesize { + type Error = TryFromFloatError; + + #[inline] + fn try_from(value: f64) -> Result { + if i64::MIN as f64 <= value && value <= i64::MAX as f64 { + Ok(Self(value as i64)) + } else { + Err(TryFromFloatError(())) + } + } +} + +impl TryFrom for Filesize { + type Error = TryFromFloatError; + + #[inline] + fn try_from(value: f32) -> Result { + if i64::MIN as f32 <= value && value <= i64::MAX as f32 { + Ok(Self(value as i64)) + } else { + Err(TryFromFloatError(())) + } + } +} + impl FromValue for Filesize { fn from_value(value: Value) -> Result { value.as_filesize() @@ -260,7 +272,7 @@ impl fmt::Display for Filesize { /// All the possible filesize units for a [`Filesize`]. /// -/// This type contains both units with metric (SI) decimal prefixes which are powers of 10 (e.g., KB = 1000 bytes) +/// This type contains both units with metric (SI) decimal prefixes which are powers of 10 (e.g., kB = 1000 bytes) /// and units with binary prefixes which are powers of 2 (e.g., KiB = 1024 bytes). /// /// The number of bytes in a [`FilesizeUnit`] can be obtained using @@ -330,13 +342,13 @@ impl FilesizeUnit { /// ``` /// # use nu_protocol::FilesizeUnit; /// assert_eq!(FilesizeUnit::B.as_str(), "B"); - /// assert_eq!(FilesizeUnit::KB.as_str(), "KB"); + /// assert_eq!(FilesizeUnit::KB.as_str(), "kB"); /// assert_eq!(FilesizeUnit::KiB.as_str(), "KiB"); /// ``` pub const fn as_str(&self) -> &'static str { match self { Self::B => "B", - Self::KB => "KB", + Self::KB => "kB", Self::MB => "MB", Self::GB => "GB", Self::TB => "TB", @@ -397,7 +409,7 @@ impl FromStr for FilesizeUnit { fn from_str(s: &str) -> Result { Ok(match s { "B" => Self::B, - "KB" => Self::KB, + "kB" => Self::KB, "MB" => Self::MB, "GB" => Self::GB, "TB" => Self::TB, diff --git a/crates/nuon/src/lib.rs b/crates/nuon/src/lib.rs index 304c8f977c..135788d69c 100644 --- a/crates/nuon/src/lib.rs +++ b/crates/nuon/src/lib.rs @@ -154,8 +154,8 @@ 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),); + nuon_end_to_end("1024B", Some(Value::test_filesize(1024))); + assert_eq!(from_nuon("1kib", None).unwrap(), Value::test_filesize(1024)); } #[test] diff --git a/crates/nuon/src/to.rs b/crates/nuon/src/to.rs index b6c4f7829b..10850f4313 100644 --- a/crates/nuon/src/to.rs +++ b/crates/nuon/src/to.rs @@ -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() {