mirror of
https://github.com/nushell/nushell.git
synced 2025-05-14 19:54:38 +00:00
154 lines
4.5 KiB
Rust
154 lines
4.5 KiB
Rust
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, PipelineMetadata,
|
|
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 search_terms(&self) -> Vec<&str> {
|
|
vec!["convert", "display", "pattern", "file", "size"]
|
|
}
|
|
|
|
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 input_metadata = input.metadata();
|
|
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, input_metadata)
|
|
}
|
|
_ => 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,
|
|
input_metadata: Option<PipelineMetadata>,
|
|
) -> 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();
|
|
|
|
let result = Value::List {
|
|
vals: records,
|
|
span: input_span,
|
|
}
|
|
.into_pipeline_data();
|
|
Ok(result.set_metadata(input_metadata))
|
|
}
|
|
|
|
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)
|
|
}
|
|
}
|