mirror of
https://github.com/nushell/nushell.git
synced 2025-08-10 06:58:36 +02:00
Refactor: introduce general operate
commands to reduce duplicate code (#6879)
* make format filesize more flexible * make code simpler * finish refactor on bytes commands * finish refactor on str commands * fimplify code * rename from column_paths to cell_paths
This commit is contained in:
@ -1,11 +1,22 @@
|
||||
use crate::input_handler::{operate, CmdArgument};
|
||||
use nu_engine::CallExt;
|
||||
use nu_protocol::ast::Call;
|
||||
use nu_protocol::ast::{Call, CellPath};
|
||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||
use nu_protocol::{
|
||||
format_filesize, Category, Example, IntoPipelineData, PipelineData, PipelineMetadata,
|
||||
ShellError, Signature, Span, SyntaxShape, Value,
|
||||
format_filesize, Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape,
|
||||
Value,
|
||||
};
|
||||
use std::iter;
|
||||
|
||||
struct Arguments {
|
||||
format_value: String,
|
||||
cell_paths: Option<Vec<CellPath>>,
|
||||
}
|
||||
|
||||
impl CmdArgument for Arguments {
|
||||
fn take_cell_paths(&mut self) -> Option<Vec<CellPath>> {
|
||||
self.cell_paths.take()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct FileSize;
|
||||
@ -17,15 +28,15 @@ impl Command for 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",
|
||||
"the format into which convert the file sizes",
|
||||
)
|
||||
.rest(
|
||||
"rest",
|
||||
SyntaxShape::CellPath,
|
||||
"optinally find and replace text by column paths",
|
||||
)
|
||||
.category(Category::Strings)
|
||||
}
|
||||
@ -45,98 +56,62 @@ impl Command for FileSize {
|
||||
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)?
|
||||
.req::<Value>(engine_state, stack, 0)?
|
||||
.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()),
|
||||
}
|
||||
let cell_paths: Vec<CellPath> = call.rest(engine_state, stack, 1)?;
|
||||
let cell_paths = (!cell_paths.is_empty()).then(|| cell_paths);
|
||||
let arg = Arguments {
|
||||
format_value,
|
||||
cell_paths,
|
||||
};
|
||||
operate(
|
||||
format_value_impl,
|
||||
arg,
|
||||
input,
|
||||
call.head,
|
||||
engine_state.ctrlc.clone(),
|
||||
)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![
|
||||
Example {
|
||||
description: "Convert the size row to KB",
|
||||
example: "ls | format filesize size KB",
|
||||
example: "ls | format filesize KB size",
|
||||
result: None,
|
||||
},
|
||||
Example {
|
||||
description: "Convert the apparent row to B",
|
||||
example: "du | format filesize apparent B",
|
||||
example: "du | format filesize B apparent",
|
||||
result: None,
|
||||
},
|
||||
Example {
|
||||
description: "Convert the size data to MB",
|
||||
example: "4Gb | format filesize MB",
|
||||
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 {
|
||||
fn format_value_impl(val: &Value, arg: &Arguments, 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,
|
||||
val: format_filesize(*val, &arg.format_value, false),
|
||||
span: *span,
|
||||
},
|
||||
other => Value::Error {
|
||||
error: ShellError::UnsupportedInput(
|
||||
format!(
|
||||
"Input's type is not supported, support type: <filesize>, current_type: {}",
|
||||
other.get_type()
|
||||
),
|
||||
span,
|
||||
),
|
||||
},
|
||||
_ => Value::Nothing { span },
|
||||
}
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user