nu_command/strings/format/
filesize.rs1use 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 float_precision: usize,
10 cell_paths: Option<Vec<CellPath>>,
11}
12
13impl CmdArgument for Arguments {
14 fn take_cell_paths(&mut self) -> Option<Vec<CellPath>> {
15 self.cell_paths.take()
16 }
17}
18
19#[derive(Clone)]
20pub struct FormatFilesize;
21
22impl Command for FormatFilesize {
23 fn name(&self) -> &str {
24 "format filesize"
25 }
26
27 fn signature(&self) -> Signature {
28 Signature::build("format filesize")
29 .input_output_types(vec![
30 (Type::Filesize, Type::String),
31 (Type::table(), Type::table()),
32 (Type::record(), Type::record()),
33 ])
34 .allow_variants_without_examples(true)
35 .required(
36 "format value",
37 SyntaxShape::String,
38 "The format into which convert the file sizes.",
39 )
40 .rest(
41 "rest",
42 SyntaxShape::CellPath,
43 "For a data structure input, format filesizes at the given cell paths.",
44 )
45 .category(Category::Strings)
46 }
47
48 fn description(&self) -> &str {
49 "Converts a column of filesizes to some specified format."
50 }
51
52 fn extra_description(&self) -> &str {
53 "Decimal precision is controlled by `$env.config.float_precision`."
54 }
55
56 fn search_terms(&self) -> Vec<&str> {
57 vec!["convert", "display"]
58 }
59
60 fn is_const(&self) -> bool {
61 true
62 }
63
64 fn run(
65 &self,
66 engine_state: &EngineState,
67 stack: &mut Stack,
68 call: &Call,
69 input: PipelineData,
70 ) -> Result<PipelineData, ShellError> {
71 let unit = parse_filesize_unit(call.req::<Spanned<String>>(engine_state, stack, 0)?)?;
72 let cell_paths: Vec<CellPath> = call.rest(engine_state, stack, 1)?;
73 let cell_paths = (!cell_paths.is_empty()).then_some(cell_paths);
74 let float_precision = stack.get_config(engine_state).float_precision.max(0) as usize;
76 let arg = Arguments {
77 unit,
78 float_precision,
79 cell_paths,
80 };
81 operate(
82 format_value_impl,
83 arg,
84 input,
85 call.head,
86 engine_state.signals(),
87 )
88 }
89
90 fn run_const(
91 &self,
92 working_set: &StateWorkingSet,
93 call: &Call,
94 input: PipelineData,
95 ) -> Result<PipelineData, ShellError> {
96 let unit = parse_filesize_unit(call.req_const::<Spanned<String>>(working_set, 0)?)?;
97 let cell_paths: Vec<CellPath> = call.rest_const(working_set, 1)?;
98 let cell_paths = (!cell_paths.is_empty()).then_some(cell_paths);
99 let float_precision = working_set.permanent().get_config().float_precision.max(0) as usize;
100 let arg = Arguments {
101 unit,
102 float_precision,
103 cell_paths,
104 };
105 operate(
106 format_value_impl,
107 arg,
108 input,
109 call.head,
110 working_set.permanent().signals(),
111 )
112 }
113
114 fn examples(&self) -> Vec<Example<'_>> {
115 vec![
116 Example {
117 description: "Convert the size column to KB.",
118 example: "ls | format filesize kB size",
119 result: None,
120 },
121 Example {
122 description: "Convert the apparent column to B.",
123 example: "du | format filesize B apparent",
124 result: None,
125 },
126 Example {
127 description: "Convert the size data to MB.",
128 example: "4GB | format filesize MB",
129 result: Some(Value::test_string("4000 MB")),
130 },
131 ]
132 }
133}
134
135fn parse_filesize_unit(format: Spanned<String>) -> Result<FilesizeUnit, ShellError> {
136 format.item.parse().map_err(|_| ShellError::InvalidUnit {
137 supported_units: SUPPORTED_FILESIZE_UNITS.join(", "),
138 span: format.span,
139 })
140}
141
142fn format_value_impl(val: &Value, arg: &Arguments, span: Span) -> Value {
143 let value_span = val.span();
144 match val {
145 Value::Filesize { val, .. } => {
146 let bytes: i64 = (*val).into();
149 let unit_bytes = arg.unit.as_bytes() as i64;
150 let has_remainder =
151 arg.unit != FilesizeUnit::B && unit_bytes > 0 && (bytes % unit_bytes) != 0;
152
153 let precision = if has_remainder {
154 Some(arg.float_precision)
155 } else {
156 None
157 };
158
159 FilesizeFormatter::new()
160 .unit(arg.unit)
161 .precision(precision)
162 .format(*val)
163 .to_string()
164 .into_value(span)
165 }
166 Value::Error { .. } => val.clone(),
167 _ => Value::error(
168 ShellError::OnlySupportsThisInputType {
169 exp_input_type: "filesize".into(),
170 wrong_type: val.get_type().to_string(),
171 dst_span: span,
172 src_span: value_span,
173 },
174 span,
175 ),
176 }
177}
178
179#[cfg(test)]
180mod tests {
181 use super::*;
182
183 #[test]
184 fn test_examples() {
185 use crate::test_examples;
186
187 test_examples(FormatFilesize)
188 }
189}