mirror of
https://github.com/nushell/nushell.git
synced 2024-11-21 16:03:19 +01:00
Add filesize precision
This commit is contained in:
parent
fa017d32a7
commit
2c74c679b5
@ -141,7 +141,7 @@ fn local_into_string(value: Value, separator: &str, config: &Config) -> String {
|
||||
Value::Bool { val, .. } => val.to_string(),
|
||||
Value::Int { val, .. } => val.to_string(),
|
||||
Value::Float { val, .. } => val.to_string(),
|
||||
Value::Filesize { val, .. } => config.filesize.unit.display(val).to_string(),
|
||||
Value::Filesize { val, .. } => config.filesize.display(val).to_string(),
|
||||
Value::Duration { val, .. } => format_duration(val),
|
||||
Value::Date { val, .. } => {
|
||||
format!("{} ({})", val.to_rfc2822(), HumanTime::from(val))
|
||||
|
@ -46,12 +46,7 @@ impl Command for SubCommand {
|
||||
Value::Filesize { val, .. } => {
|
||||
usize::try_from(val).map_err(|_| ShellError::InvalidValue {
|
||||
valid: "a non-negative int or filesize".into(),
|
||||
actual: engine_state
|
||||
.get_config()
|
||||
.filesize
|
||||
.unit
|
||||
.display(val)
|
||||
.to_string(),
|
||||
actual: engine_state.get_config().filesize.display(val).to_string(),
|
||||
span: length_val.span(),
|
||||
})
|
||||
}
|
||||
|
@ -83,12 +83,7 @@ fn chars(
|
||||
Value::Filesize { val, .. } => {
|
||||
usize::try_from(val).map_err(|_| ShellError::InvalidValue {
|
||||
valid: "a non-negative int or filesize".into(),
|
||||
actual: engine_state
|
||||
.get_config()
|
||||
.filesize
|
||||
.unit
|
||||
.display(val)
|
||||
.to_string(),
|
||||
actual: engine_state.get_config().filesize.display(val).to_string(),
|
||||
span: length_val.span(),
|
||||
})
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
use super::{config_update_string_enum, prelude::*};
|
||||
use crate::{self as nu_protocol, DisplayFilesize, Filesize, FilesizeUnit};
|
||||
use crate::{DisplayFilesize, Filesize, FilesizeUnit};
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub enum FilesizeFormatUnit {
|
||||
@ -8,17 +8,6 @@ pub enum FilesizeFormatUnit {
|
||||
Unit(FilesizeUnit),
|
||||
}
|
||||
|
||||
impl FilesizeFormatUnit {
|
||||
pub fn display(&self, filesize: Filesize) -> DisplayFilesize {
|
||||
let unit = match self {
|
||||
Self::Decimal => filesize.largest_decimal_unit(),
|
||||
Self::Binary => filesize.largest_binary_unit(),
|
||||
Self::Unit(unit) => *unit,
|
||||
};
|
||||
filesize.display(unit)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<FilesizeUnit> for FilesizeFormatUnit {
|
||||
fn from(unit: FilesizeUnit) -> Self {
|
||||
Self::Unit(unit)
|
||||
@ -54,15 +43,28 @@ impl IntoValue for FilesizeFormatUnit {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, IntoValue, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct FilesizeConfig {
|
||||
pub unit: FilesizeFormatUnit,
|
||||
pub precision: u64,
|
||||
}
|
||||
|
||||
impl FilesizeConfig {
|
||||
pub fn display(&self, filesize: Filesize) -> DisplayFilesize {
|
||||
let unit = match self.unit {
|
||||
FilesizeFormatUnit::Decimal => filesize.largest_decimal_unit(),
|
||||
FilesizeFormatUnit::Binary => filesize.largest_binary_unit(),
|
||||
FilesizeFormatUnit::Unit(unit) => unit,
|
||||
};
|
||||
filesize.display(unit).precision(self.precision as usize)
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for FilesizeConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
unit: FilesizeFormatUnit::Decimal,
|
||||
precision: 1,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -83,6 +85,7 @@ impl UpdateFromValue for FilesizeConfig {
|
||||
let path = &mut path.push(col);
|
||||
match col.as_str() {
|
||||
"unit" => config_update_string_enum(&mut self.unit, val, path, errors),
|
||||
"precision" => self.precision.update(value, path, errors),
|
||||
"format" | "metric" => {
|
||||
// TODO: remove after next release
|
||||
errors.deprecated_option(path, "set $env.config.filesize.unit", val.span())
|
||||
@ -92,3 +95,13 @@ impl UpdateFromValue for FilesizeConfig {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoValue for FilesizeConfig {
|
||||
fn into_value(self, span: Span) -> Value {
|
||||
record! {
|
||||
"unit" => self.unit.into_value(span),
|
||||
"precision" => (self.precision as i64).into_value(span),
|
||||
}
|
||||
.into_value(span)
|
||||
}
|
||||
}
|
||||
|
@ -91,6 +91,20 @@ impl UpdateFromValue for i64 {
|
||||
}
|
||||
}
|
||||
|
||||
impl UpdateFromValue for u64 {
|
||||
fn update(&mut self, value: &Value, path: &mut ConfigPath, errors: &mut ConfigErrors) {
|
||||
if let Ok(val) = value.as_int() {
|
||||
if let Ok(val) = val.try_into() {
|
||||
*self = val;
|
||||
} else {
|
||||
errors.invalid_value(path, "a non-negative integer", value);
|
||||
}
|
||||
} else {
|
||||
errors.type_mismatch(path, Type::Int, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl UpdateFromValue for usize {
|
||||
fn update(&mut self, value: &Value, path: &mut ConfigPath, errors: &mut ConfigErrors) {
|
||||
if let Ok(val) = value.as_int() {
|
||||
|
@ -162,6 +162,7 @@ impl Filesize {
|
||||
DisplayFilesize {
|
||||
filesize: *self,
|
||||
unit,
|
||||
precision: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -529,11 +530,24 @@ impl fmt::Display for FilesizeUnit {
|
||||
pub struct DisplayFilesize {
|
||||
filesize: Filesize,
|
||||
unit: FilesizeUnit,
|
||||
precision: Option<usize>,
|
||||
}
|
||||
|
||||
impl DisplayFilesize {
|
||||
pub fn precision(mut self, precision: usize) -> Self {
|
||||
self.precision = Some(precision);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for DisplayFilesize {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let Self { filesize, unit } = *self;
|
||||
let Self {
|
||||
filesize,
|
||||
unit,
|
||||
precision,
|
||||
} = *self;
|
||||
let precision = precision.or(f.precision());
|
||||
match unit {
|
||||
FilesizeUnit::B => write!(f, "{} B", filesize.0),
|
||||
FilesizeUnit::KiB
|
||||
@ -542,8 +556,13 @@ impl fmt::Display for DisplayFilesize {
|
||||
| FilesizeUnit::TiB
|
||||
| FilesizeUnit::PiB
|
||||
| FilesizeUnit::EiB => {
|
||||
let val = filesize.0 as f64 / unit.as_bytes() as f64;
|
||||
// This won't give exact results for large filesizes and/or units.
|
||||
write!(f, "{} {unit}", filesize.0 as f64 / unit.as_bytes() as f64)
|
||||
if let Some(precision) = precision {
|
||||
write!(f, "{val:.precision$} {unit}")
|
||||
} else {
|
||||
write!(f, "{val} {unit}")
|
||||
}
|
||||
}
|
||||
FilesizeUnit::KB
|
||||
| FilesizeUnit::GB
|
||||
@ -554,31 +573,37 @@ impl fmt::Display for DisplayFilesize {
|
||||
// Format an exact, possibly fractional, string representation of `filesize`.
|
||||
let bytes = unit.as_bytes() as i64;
|
||||
let whole = filesize.0 / bytes;
|
||||
let mut fract = (filesize.0 % bytes).unsigned_abs();
|
||||
if fract == 0 || f.precision() == Some(0) {
|
||||
let fract = (filesize.0 % bytes).unsigned_abs();
|
||||
if fract == 0 || precision == Some(0) {
|
||||
write!(f, "{whole} {unit}")
|
||||
} else {
|
||||
// fract <= bytes by nature of `%` and bytes <= EB = 10 ^ 18
|
||||
// So, the longest string for the fractional portion can be 18 characters.
|
||||
let buf = &mut [b'0'; 18];
|
||||
for d in buf.iter_mut().rev() {
|
||||
*d += (fract % 10) as u8;
|
||||
fract /= 10;
|
||||
if fract == 0 {
|
||||
let stop = precision.unwrap_or(buf.len());
|
||||
let mut fract = fract;
|
||||
let mut power = bytes.unsigned_abs() / 10;
|
||||
let mut i = 0;
|
||||
loop {
|
||||
let q = fract / power;
|
||||
let r = fract % power;
|
||||
debug_assert!(q < 10);
|
||||
buf[i] += q as u8;
|
||||
i += 1;
|
||||
if r == 0 || i >= stop {
|
||||
break;
|
||||
}
|
||||
fract = r;
|
||||
power /= 10;
|
||||
}
|
||||
|
||||
let power = bytes.ilog10() as usize;
|
||||
debug_assert_eq!(bytes, 10_i64.pow(power as u32), "an exact power of 10");
|
||||
// Safety: all the characters in `buf` are valid UTF-8.
|
||||
let fract =
|
||||
unsafe { std::str::from_utf8_unchecked(&buf[(buf.len() - power)..]) };
|
||||
let fract = unsafe { std::str::from_utf8_unchecked(&buf[..i]) };
|
||||
|
||||
match f.precision() {
|
||||
Some(p) if p <= power => write!(f, "{whole}.{} {unit}", &fract[..p]),
|
||||
Some(p) => write!(f, "{whole}.{fract:0<p$} {unit}"),
|
||||
None => write!(f, "{whole}.{} {unit}", fract.trim_end_matches('0')),
|
||||
if let Some(p) = precision {
|
||||
write!(f, "{whole}.{fract:0<p$} {unit}")
|
||||
} else {
|
||||
write!(f, "{whole}.{fract} {unit}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -837,7 +837,7 @@ impl Value {
|
||||
Value::Bool { val, .. } => val.to_string(),
|
||||
Value::Int { val, .. } => val.to_string(),
|
||||
Value::Float { val, .. } => val.to_string(),
|
||||
Value::Filesize { val, .. } => config.filesize.unit.display(*val).to_string(),
|
||||
Value::Filesize { val, .. } => config.filesize.display(*val).to_string(),
|
||||
Value::Duration { val, .. } => format_duration(*val),
|
||||
Value::Date { val, .. } => match &config.datetime_format.normal {
|
||||
Some(format) => self.format_datetime(val, format),
|
||||
|
@ -231,6 +231,7 @@ $env.config = {
|
||||
# You can also set this to a particular unit to use that unit to display all file sizes.
|
||||
# The available units are: B, kB, KiB, MB, MiB, GB, GiB, TB, TiB, PB, PiB, EB, or EiB.
|
||||
unit: decimal
|
||||
precision: 1 # the number of digits to display after the decimal point for file sizes
|
||||
}
|
||||
|
||||
cursor_shape: {
|
||||
|
Loading…
Reference in New Issue
Block a user