add format filesize (#5498)

* add format filesize

* add comment

* add comment

* remove comment
This commit is contained in:
WindSoilder
2022-05-10 19:35:14 +08:00
committed by GitHub
parent e4959d2f9f
commit 8030f7e9f0
5 changed files with 173 additions and 24 deletions

View File

@ -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<PipelineData, ShellError> {
let field = call.req::<Value>(engine_state, stack, 0)?.as_string()?;
let format_value = call
.req::<Value>(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<Example> {
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<Value>,
field: String,
format_value: String,
input_span: Span,
) -> Result<PipelineData, ShellError> {
let records: Vec<Value> = 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)
}
}

View File

@ -1,3 +1,5 @@
pub mod command;
mod filesize;
pub use self::filesize::FileSize;
pub use command::Format;