nu_command/strings/format/
filesize.rs

1use nu_cmd_base::input_handler::{CmdArgument, operate};
2use nu_engine::command_prelude::*;
3use nu_protocol::{
4    FilesizeFormatter, FilesizeUnit, SUPPORTED_FILESIZE_UNITS, engine::StateWorkingSet,
5};
6
7struct Arguments {
8    unit: FilesizeUnit,
9    cell_paths: Option<Vec<CellPath>>,
10}
11
12impl CmdArgument for Arguments {
13    fn take_cell_paths(&mut self) -> Option<Vec<CellPath>> {
14        self.cell_paths.take()
15    }
16}
17
18#[derive(Clone)]
19pub struct FormatFilesize;
20
21impl Command for FormatFilesize {
22    fn name(&self) -> &str {
23        "format filesize"
24    }
25
26    fn signature(&self) -> Signature {
27        Signature::build("format filesize")
28            .input_output_types(vec![
29                (Type::Filesize, Type::String),
30                (Type::table(), Type::table()),
31                (Type::record(), Type::record()),
32            ])
33            .allow_variants_without_examples(true)
34            .required(
35                "format value",
36                SyntaxShape::String,
37                "The format into which convert the file sizes.",
38            )
39            .rest(
40                "rest",
41                SyntaxShape::CellPath,
42                "For a data structure input, format filesizes at the given cell paths.",
43            )
44            .category(Category::Strings)
45    }
46
47    fn description(&self) -> &str {
48        "Converts a column of filesizes to some specified format."
49    }
50
51    fn search_terms(&self) -> Vec<&str> {
52        vec!["convert", "display", "pattern", "human readable"]
53    }
54
55    fn is_const(&self) -> bool {
56        true
57    }
58
59    fn run(
60        &self,
61        engine_state: &EngineState,
62        stack: &mut Stack,
63        call: &Call,
64        input: PipelineData,
65    ) -> Result<PipelineData, ShellError> {
66        let unit = parse_filesize_unit(call.req::<Spanned<String>>(engine_state, stack, 0)?)?;
67        let cell_paths: Vec<CellPath> = call.rest(engine_state, stack, 1)?;
68        let cell_paths = (!cell_paths.is_empty()).then_some(cell_paths);
69        let arg = Arguments { unit, cell_paths };
70        operate(
71            format_value_impl,
72            arg,
73            input,
74            call.head,
75            engine_state.signals(),
76        )
77    }
78
79    fn run_const(
80        &self,
81        working_set: &StateWorkingSet,
82        call: &Call,
83        input: PipelineData,
84    ) -> Result<PipelineData, ShellError> {
85        let unit = parse_filesize_unit(call.req_const::<Spanned<String>>(working_set, 0)?)?;
86        let cell_paths: Vec<CellPath> = call.rest_const(working_set, 1)?;
87        let cell_paths = (!cell_paths.is_empty()).then_some(cell_paths);
88        let arg = Arguments { unit, cell_paths };
89        operate(
90            format_value_impl,
91            arg,
92            input,
93            call.head,
94            working_set.permanent().signals(),
95        )
96    }
97
98    fn examples(&self) -> Vec<Example> {
99        vec![
100            Example {
101                description: "Convert the size column to KB",
102                example: "ls | format filesize KB size",
103                result: None,
104            },
105            Example {
106                description: "Convert the apparent column to B",
107                example: "du | format filesize B apparent",
108                result: None,
109            },
110            Example {
111                description: "Convert the size data to MB",
112                example: "4GB | format filesize MB",
113                result: Some(Value::test_string("4000 MB")),
114            },
115        ]
116    }
117}
118
119fn parse_filesize_unit(format: Spanned<String>) -> Result<FilesizeUnit, ShellError> {
120    format.item.parse().map_err(|_| ShellError::InvalidUnit {
121        supported_units: SUPPORTED_FILESIZE_UNITS.join(", "),
122        span: format.span,
123    })
124}
125
126fn format_value_impl(val: &Value, arg: &Arguments, span: Span) -> Value {
127    let value_span = val.span();
128    match val {
129        Value::Filesize { val, .. } => FilesizeFormatter::new()
130            .unit(arg.unit)
131            .format(*val)
132            .to_string()
133            .into_value(span),
134        Value::Error { .. } => val.clone(),
135        _ => Value::error(
136            ShellError::OnlySupportsThisInputType {
137                exp_input_type: "filesize".into(),
138                wrong_type: val.get_type().to_string(),
139                dst_span: span,
140                src_span: value_span,
141            },
142            span,
143        ),
144    }
145}
146
147#[cfg(test)]
148mod tests {
149    use super::*;
150
151    #[test]
152    fn test_examples() {
153        use crate::test_examples;
154
155        test_examples(FormatFilesize)
156    }
157}