diff --git a/crates/nu-command/src/default_context.rs b/crates/nu-command/src/default_context.rs index 3ee0f1edf..a193c67cc 100644 --- a/crates/nu-command/src/default_context.rs +++ b/crates/nu-command/src/default_context.rs @@ -162,6 +162,7 @@ pub fn create_default_context(cwd: impl AsRef) -> EngineState { Decode, DetectColumns, Format, + FileSize, Parse, Size, Split, diff --git a/crates/nu-command/src/strings/format/filesize.rs b/crates/nu-command/src/strings/format/filesize.rs new file mode 100644 index 000000000..508c569a8 --- /dev/null +++ b/crates/nu-command/src/strings/format/filesize.rs @@ -0,0 +1,144 @@ +use nu_engine::CallExt; +use nu_protocol::ast::Call; +use nu_protocol::engine::{Command, EngineState, Stack}; +use nu_protocol::{ + format_filesize, Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, + Span, SyntaxShape, Value, +}; +use std::iter; + +#[derive(Clone)] +pub struct FileSize; + +impl Command for FileSize { + fn name(&self) -> &str { + "format filesize" + } + + fn signature(&self) -> Signature { + Signature::build("format filesize") + .required( + "field", + SyntaxShape::String, + "the name of the column to update", + ) + .required( + "format value", + SyntaxShape::String, + "the format into which convert the filesizes", + ) + .category(Category::Strings) + } + + fn usage(&self) -> &str { + "Converts a column of filesizes to some specified format" + } + + fn run( + &self, + engine_state: &EngineState, + stack: &mut Stack, + call: &Call, + input: PipelineData, + ) -> Result { + let field = call.req::(engine_state, stack, 0)?.as_string()?; + let format_value = call + .req::(engine_state, stack, 1)? + .as_string()? + .to_ascii_lowercase(); + let span = call.head; + let data_as_value = input.into_value(span); + + // Something need to consider: + // 1. what if input data type is not table? For now just output nothing. + // 2. what if value is not a FileSize type? For now just return nothing too for the value. + match data_as_value { + Value::List { vals, span } => format_impl(vals, field, format_value, span), + _ => Ok(Value::Nothing { span }.into_pipeline_data()), + } + } + + fn examples(&self) -> Vec { + vec![ + Example { + description: "Convert the size row to KB", + example: "ls | format filesize size KB", + result: None, + }, + Example { + description: "Convert the apparent row to B", + example: "du | format filesize apparent B", + result: None, + }, + ] + } +} + +fn format_impl( + vals: Vec, + field: String, + format_value: String, + input_span: Span, +) -> Result { + let records: Vec = vals + .into_iter() + .map(|rec| { + let record_span = rec.span(); + match rec { + Value::Record { cols, vals, span } => { + let mut new_cols = vec![]; + let mut new_vals = vec![]; + for (c, v) in iter::zip(cols, vals) { + // find column to format, try format the value. + if c == field { + new_vals.push(format_value_impl(v, &format_value, span)); + } else { + new_vals.push(v); + } + new_cols.push(c); + } + Value::Record { + cols: new_cols, + vals: new_vals, + span, + } + } + _ => Value::Nothing { + span: match record_span { + Ok(s) => s, + Err(_) => input_span, + }, + }, + } + }) + .collect(); + + Ok(Value::List { + vals: records, + span: input_span, + } + .into_pipeline_data()) +} + +fn format_value_impl(val: Value, format_value: &str, span: Span) -> Value { + match val { + Value::Filesize { val, span } => Value::String { + // don't need to concern about metric, we just format units by what user input. + val: format_filesize(val, format_value, false), + span, + }, + _ => Value::Nothing { span }, + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_examples() { + use crate::test_examples; + + test_examples(FileSize) + } +} diff --git a/crates/nu-command/src/strings/format/mod.rs b/crates/nu-command/src/strings/format/mod.rs index 71be06ceb..a6c001379 100644 --- a/crates/nu-command/src/strings/format/mod.rs +++ b/crates/nu-command/src/strings/format/mod.rs @@ -1,3 +1,5 @@ pub mod command; +mod filesize; +pub use self::filesize::FileSize; pub use command::Format; diff --git a/crates/nu-command/tests/commands/format.rs b/crates/nu-command/tests/commands/format.rs index 58766393e..128f89c32 100644 --- a/crates/nu-command/tests/commands/format.rs +++ b/crates/nu-command/tests/commands/format.rs @@ -46,8 +46,6 @@ fn can_use_variables() { assert_eq!(actual.out, "nu is a new type of shell"); } -// FIXME: jt: needs more work -#[ignore] #[test] fn format_filesize_works() { Playground::setup("format_filesize_test_1", |dirs, sandbox| { @@ -71,8 +69,6 @@ fn format_filesize_works() { }) } -// FIXME: jt: needs more work -#[ignore] #[test] fn format_filesize_works_with_nonempty_files() { Playground::setup( diff --git a/crates/nu-protocol/src/value/mod.rs b/crates/nu-protocol/src/value/mod.rs index 655467057..df7452a69 100644 --- a/crates/nu-protocol/src/value/mod.rs +++ b/crates/nu-protocol/src/value/mod.rs @@ -458,7 +458,7 @@ impl Value { Value::Bool { val, .. } => val.to_string(), Value::Int { val, .. } => val.to_string(), Value::Float { val, .. } => val.to_string(), - Value::Filesize { val, .. } => format_filesize(*val, config), + Value::Filesize { val, .. } => format_filesize_from_conf(*val, config), Value::Duration { val, .. } => format_duration(*val), Value::Date { val, .. } => format!("{} ({})", val.to_rfc2822(), HumanTime::from(*val)), Value::Range { val, .. } => { @@ -499,7 +499,7 @@ impl Value { Value::Bool { val, .. } => val.to_string(), Value::Int { val, .. } => val.to_string(), Value::Float { val, .. } => val.to_string(), - Value::Filesize { val, .. } => format_filesize(*val, config), + Value::Filesize { val, .. } => format_filesize_from_conf(*val, config), Value::Duration { val, .. } => format_duration(*val), Value::Date { val, .. } => HumanTime::from(*val).to_string(), Value::Range { val, .. } => { @@ -547,7 +547,7 @@ impl Value { Value::Bool { val, .. } => val.to_string(), Value::Int { val, .. } => val.to_string(), Value::Float { val, .. } => val.to_string(), - Value::Filesize { val, .. } => format_filesize(*val, config), + Value::Filesize { val, .. } => format_filesize_from_conf(*val, config), Value::Duration { val, .. } => format_duration(*val), Value::Date { val, .. } => format!("{:?}", val), Value::Range { val, .. } => { @@ -2325,14 +2325,24 @@ pub fn format_duration(duration: i64) -> String { ) } -fn format_filesize(num_bytes: i64, config: &Config) -> String { +fn format_filesize_from_conf(num_bytes: i64, config: &Config) -> String { + // We need to take into account config.filesize_metric so, if someone asks for KB + // filesize_metric is true, return KiB + format_filesize( + num_bytes, + config.filesize_format.as_str(), + config.filesize_metric, + ) +} + +pub fn format_filesize(num_bytes: i64, format_value: &str, filesize_metric: bool) -> String { // Allow the user to specify how they want their numbers formatted - let filesize_format_var = get_config_filesize_format(config); + let filesize_format_var = get_filesize_format(format_value, filesize_metric); let byte = byte_unit::Byte::from_bytes(num_bytes as u128); let adj_byte = if filesize_format_var.0 == byte_unit::ByteUnit::B && filesize_format_var.1 == "auto" { - byte.get_appropriate_unit(!config.filesize_metric) + byte.get_appropriate_unit(!filesize_metric) } else { byte.get_adjusted_unit(filesize_format_var.0) }; @@ -2371,13 +2381,11 @@ fn format_filesize(num_bytes: i64, config: &Config) -> String { } } -fn get_config_filesize_format(config: &Config) -> (ByteUnit, &str) { - // We need to take into account config.filesize_metric so, if someone asks for KB - // filesize_metric is true, return KiB - let filesize_format = match config.filesize_format.as_str() { +fn get_filesize_format(format_value: &str, filesize_metric: bool) -> (ByteUnit, &str) { + match format_value { "b" => (byte_unit::ByteUnit::B, ""), "kb" => { - if config.filesize_metric { + if filesize_metric { (byte_unit::ByteUnit::KiB, "") } else { (byte_unit::ByteUnit::KB, "") @@ -2385,7 +2393,7 @@ fn get_config_filesize_format(config: &Config) -> (ByteUnit, &str) { } "kib" => (byte_unit::ByteUnit::KiB, ""), "mb" => { - if config.filesize_metric { + if filesize_metric { (byte_unit::ByteUnit::MiB, "") } else { (byte_unit::ByteUnit::MB, "") @@ -2393,7 +2401,7 @@ fn get_config_filesize_format(config: &Config) -> (ByteUnit, &str) { } "mib" => (byte_unit::ByteUnit::MiB, ""), "gb" => { - if config.filesize_metric { + if filesize_metric { (byte_unit::ByteUnit::GiB, "") } else { (byte_unit::ByteUnit::GB, "") @@ -2401,7 +2409,7 @@ fn get_config_filesize_format(config: &Config) -> (ByteUnit, &str) { } "gib" => (byte_unit::ByteUnit::GiB, ""), "tb" => { - if config.filesize_metric { + if filesize_metric { (byte_unit::ByteUnit::TiB, "") } else { (byte_unit::ByteUnit::TB, "") @@ -2409,7 +2417,7 @@ fn get_config_filesize_format(config: &Config) -> (ByteUnit, &str) { } "tib" => (byte_unit::ByteUnit::TiB, ""), "pb" => { - if config.filesize_metric { + if filesize_metric { (byte_unit::ByteUnit::PiB, "") } else { (byte_unit::ByteUnit::PB, "") @@ -2417,7 +2425,7 @@ fn get_config_filesize_format(config: &Config) -> (ByteUnit, &str) { } "pib" => (byte_unit::ByteUnit::PiB, ""), "eb" => { - if config.filesize_metric { + if filesize_metric { (byte_unit::ByteUnit::EiB, "") } else { (byte_unit::ByteUnit::EB, "") @@ -2425,7 +2433,7 @@ fn get_config_filesize_format(config: &Config) -> (ByteUnit, &str) { } "eib" => (byte_unit::ByteUnit::EiB, ""), "zb" => { - if config.filesize_metric { + if filesize_metric { (byte_unit::ByteUnit::ZiB, "") } else { (byte_unit::ByteUnit::ZB, "") @@ -2433,9 +2441,7 @@ fn get_config_filesize_format(config: &Config) -> (ByteUnit, &str) { } "zib" => (byte_unit::ByteUnit::ZiB, ""), _ => (byte_unit::ByteUnit::B, "auto"), - }; - - filesize_format + } } #[cfg(test)]