mirror of
https://github.com/nushell/nushell.git
synced 2025-08-16 01:48:16 +02:00
small refactoring around units and add tests (#15746)
Closes #14469 # Description - ~~Implement the ``--unit`` conversion in "into int" command~~ - New ``ShellError::InvalidUnit`` unit if users enter wrong units - Made ``ShellError::CantConvertToDuration`` more generic: became ``CantConvertToUnit`` - Tried to improve the way we parse units and get the supported units. It's not complete, though, I will continue this refactoring in another PR. But I already did some small refactorings in the "format duration" and "format filesize" commands - Add tests for "format filesize" and "format duration" # User-Facing Changes ```nu ~> 1MB | format filesize sec Error: nu:🐚:invalid_unit × Invalid unit ╭─[entry #7:1:23] 1 │ 1MB | format filesize sec · ─┬─ · ╰── encountered here ╰──── help: Supported units are: B, kB, MB, GB, TB, PB, EB, KiB, MiB, GiB, TiB, PiB, EiB ```
This commit is contained in:
@ -13,7 +13,7 @@ mod pipeline;
|
||||
mod range;
|
||||
mod table;
|
||||
mod traverse;
|
||||
mod unit;
|
||||
pub mod unit;
|
||||
mod value_with_unit;
|
||||
|
||||
pub use attribute::*;
|
||||
|
@ -1,5 +1,24 @@
|
||||
use crate::{Filesize, FilesizeUnit, IntoValue, ShellError, Span, Value};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt;
|
||||
use std::str::FromStr;
|
||||
use thiserror::Error;
|
||||
|
||||
pub const SUPPORTED_DURATION_UNITS: [&str; 9] =
|
||||
["ns", "us", "µs", "ms", "sec", "min", "hr", "day", "wk"];
|
||||
|
||||
/// The error returned when failing to parse a [`Unit`].
|
||||
///
|
||||
/// This occurs when the string being parsed does not exactly match the name of one of the
|
||||
/// enum cases in [`Unit`].
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Error)]
|
||||
pub struct ParseUnitError(());
|
||||
|
||||
impl fmt::Display for ParseUnitError {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(fmt, "invalid file size or duration unit")
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub enum Unit {
|
||||
@ -78,4 +97,53 @@ impl Unit {
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the symbol [`str`] for a [`Unit`].
|
||||
///
|
||||
/// The returned string is the same exact string needed for a successful call to
|
||||
/// [`parse`](str::parse) for a [`Unit`].
|
||||
///
|
||||
/// # Examples
|
||||
/// ```
|
||||
/// # use nu_protocol::{Unit, FilesizeUnit};
|
||||
/// assert_eq!(Unit::Nanosecond.as_str(), "ns");
|
||||
/// assert_eq!(Unit::Filesize(FilesizeUnit::B).as_str(), "B");
|
||||
/// assert_eq!(Unit::Second.as_str().parse(), Ok(Unit::Second));
|
||||
/// assert_eq!(Unit::Filesize(FilesizeUnit::KB).as_str().parse(), Ok(Unit::Filesize(FilesizeUnit::KB)));
|
||||
/// ```
|
||||
pub const fn as_str(&self) -> &'static str {
|
||||
match self {
|
||||
Unit::Filesize(u) => u.as_str(),
|
||||
Unit::Nanosecond => "ns",
|
||||
Unit::Microsecond => "us",
|
||||
Unit::Millisecond => "ms",
|
||||
Unit::Second => "sec",
|
||||
Unit::Minute => "min",
|
||||
Unit::Hour => "hr",
|
||||
Unit::Day => "day",
|
||||
Unit::Week => "wk",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for Unit {
|
||||
type Err = ParseUnitError;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
if let Ok(filesize_unit) = FilesizeUnit::from_str(s) {
|
||||
return Ok(Unit::Filesize(filesize_unit));
|
||||
};
|
||||
|
||||
match s {
|
||||
"ns" => Ok(Unit::Nanosecond),
|
||||
"us" | "µs" => Ok(Unit::Microsecond),
|
||||
"ms" => Ok(Unit::Millisecond),
|
||||
"sec" => Ok(Unit::Second),
|
||||
"min" => Ok(Unit::Minute),
|
||||
"hr" => Ok(Unit::Hour),
|
||||
"day" => Ok(Unit::Day),
|
||||
"wk" => Ok(Unit::Week),
|
||||
_ => Err(ParseUnitError(())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -76,7 +76,7 @@ pub enum ShellError {
|
||||
exp_input_type: String,
|
||||
#[label("expected: {exp_input_type}")]
|
||||
dst_span: Span,
|
||||
#[label("value originates from here")]
|
||||
#[label("value originates here")]
|
||||
src_span: Span,
|
||||
},
|
||||
|
||||
@ -431,14 +431,20 @@ pub enum ShellError {
|
||||
help: Option<String>,
|
||||
},
|
||||
|
||||
#[error("Can't convert string `{details}` to duration.")]
|
||||
#[diagnostic(code(nu::shell::cant_convert_with_value))]
|
||||
CantConvertToDuration {
|
||||
details: String,
|
||||
#[label("can't be converted to duration")]
|
||||
dst_span: Span,
|
||||
#[label("this string value...")]
|
||||
src_span: Span,
|
||||
/// Failed to convert a value of one type into a different type by specifying a unit.
|
||||
///
|
||||
/// ## Resolution
|
||||
///
|
||||
/// Check that the provided value can be converted in the provided: only Durations can be converted to duration units, and only Filesize can be converted to filesize units.
|
||||
#[error("Can't convert {from_type} to the specified unit.")]
|
||||
#[diagnostic(code(nu::shell::cant_convert_value_to_unit))]
|
||||
CantConvertToUnit {
|
||||
to_type: String,
|
||||
from_type: String,
|
||||
#[label("can't convert {from_type} to {to_type}")]
|
||||
span: Span,
|
||||
#[label("conversion originates here")]
|
||||
unit_span: Span,
|
||||
#[help]
|
||||
help: Option<String>,
|
||||
},
|
||||
@ -1238,6 +1244,22 @@ This is an internal Nushell error, please file an issue https://github.com/nushe
|
||||
span: Span,
|
||||
},
|
||||
|
||||
/// Invalid unit
|
||||
///
|
||||
/// ## Resolution
|
||||
///
|
||||
/// Correct unit
|
||||
#[error("Invalid unit")]
|
||||
#[diagnostic(
|
||||
code(nu::shell::invalid_unit),
|
||||
help("Supported units are: {supported_units}")
|
||||
)]
|
||||
InvalidUnit {
|
||||
supported_units: String,
|
||||
#[label("encountered here")]
|
||||
span: Span,
|
||||
},
|
||||
|
||||
/// Tried spreading a non-list inside a list or command call.
|
||||
///
|
||||
/// ## Resolution
|
||||
|
@ -27,7 +27,7 @@ mod ty;
|
||||
mod value;
|
||||
|
||||
pub use alias::*;
|
||||
pub use ast::Unit;
|
||||
pub use ast::unit::*;
|
||||
pub use config::*;
|
||||
pub use did_you_mean::did_you_mean;
|
||||
pub use engine::{ENV_VARIABLE_ID, IN_VARIABLE_ID, NU_VARIABLE_ID};
|
||||
|
@ -10,6 +10,10 @@ use std::{
|
||||
};
|
||||
use thiserror::Error;
|
||||
|
||||
pub const SUPPORTED_FILESIZE_UNITS: [&str; 13] = [
|
||||
"B", "kB", "MB", "GB", "TB", "PB", "EB", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB",
|
||||
];
|
||||
|
||||
/// A signed number of bytes.
|
||||
///
|
||||
/// [`Filesize`] is a wrapper around [`i64`]. Whereas [`i64`] is a dimensionless value, [`Filesize`] represents a
|
||||
|
Reference in New Issue
Block a user