nu_command/strings/format/
filesize.rs1use 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}