nu_command/strings/format/
filesize.rs

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