nu_command/conversions/into/
filesize.rs1use nu_cmd_base::input_handler::{CellPathOnlyArgs, operate};
2use nu_engine::command_prelude::*;
3
4use nu_utils::get_system_locale;
5
6#[derive(Clone)]
7pub struct IntoFilesize;
8
9impl Command for IntoFilesize {
10 fn name(&self) -> &str {
11 "into filesize"
12 }
13
14 fn signature(&self) -> Signature {
15 Signature::build("into filesize")
16 .input_output_types(vec![
17 (Type::Int, Type::Filesize),
18 (Type::Number, Type::Filesize),
19 (Type::String, Type::Filesize),
20 (Type::Filesize, Type::Filesize),
21 (Type::table(), Type::table()),
22 (Type::record(), Type::record()),
23 (
24 Type::List(Box::new(Type::Int)),
25 Type::List(Box::new(Type::Filesize)),
26 ),
27 (
28 Type::List(Box::new(Type::Number)),
29 Type::List(Box::new(Type::Filesize)),
30 ),
31 (
32 Type::List(Box::new(Type::String)),
33 Type::List(Box::new(Type::Filesize)),
34 ),
35 (
36 Type::List(Box::new(Type::Filesize)),
37 Type::List(Box::new(Type::Filesize)),
38 ),
39 (
41 Type::List(Box::new(Type::Any)),
42 Type::List(Box::new(Type::Filesize)),
43 ),
44 ])
45 .allow_variants_without_examples(true)
46 .rest(
47 "rest",
48 SyntaxShape::CellPath,
49 "For a data structure input, convert data at the given cell paths.",
50 )
51 .category(Category::Conversions)
52 }
53
54 fn description(&self) -> &str {
55 "Convert value to filesize."
56 }
57
58 fn search_terms(&self) -> Vec<&str> {
59 vec!["convert", "number", "bytes"]
60 }
61
62 fn run(
63 &self,
64 engine_state: &EngineState,
65 stack: &mut Stack,
66 call: &Call,
67 input: PipelineData,
68 ) -> Result<PipelineData, ShellError> {
69 let cell_paths: Vec<CellPath> = call.rest(engine_state, stack, 0)?;
70 let args = CellPathOnlyArgs::from(cell_paths);
71 operate(action, args, input, call.head, engine_state.signals())
72 }
73
74 fn examples(&self) -> Vec<Example> {
75 vec![
76 Example {
77 description: "Convert string to filesize in table",
78 example: r#"[[device size]; ["/dev/sda1" "200"] ["/dev/loop0" "50"]] | into filesize size"#,
79 result: Some(Value::test_list(vec![
80 Value::test_record(record! {
81 "device" => Value::test_string("/dev/sda1"),
82 "size" => Value::test_filesize(200),
83 }),
84 Value::test_record(record! {
85 "device" => Value::test_string("/dev/loop0"),
86 "size" => Value::test_filesize(50),
87 }),
88 ])),
89 },
90 Example {
91 description: "Convert string to filesize",
92 example: "'2' | into filesize",
93 result: Some(Value::test_filesize(2)),
94 },
95 Example {
96 description: "Convert float to filesize",
97 example: "8.3 | into filesize",
98 result: Some(Value::test_filesize(8)),
99 },
100 Example {
101 description: "Convert int to filesize",
102 example: "5 | into filesize",
103 result: Some(Value::test_filesize(5)),
104 },
105 Example {
106 description: "Convert file size to filesize",
107 example: "4KB | into filesize",
108 result: Some(Value::test_filesize(4000)),
109 },
110 Example {
111 description: "Convert string with unit to filesize",
112 example: "'-1KB' | into filesize",
113 result: Some(Value::test_filesize(-1000)),
114 },
115 ]
116 }
117}
118
119fn action(input: &Value, _args: &CellPathOnlyArgs, span: Span) -> Value {
120 let value_span = input.span();
121 match input {
122 Value::Filesize { .. } => input.clone(),
123 Value::Int { val, .. } => Value::filesize(*val, value_span),
124 Value::Float { val, .. } => Value::filesize(*val as i64, value_span),
125 Value::String { val, .. } => match i64_from_string(val, value_span) {
126 Ok(val) => Value::filesize(val, value_span),
127 Err(error) => Value::error(error, value_span),
128 },
129 Value::Nothing { .. } => Value::filesize(0, value_span),
130 other => Value::error(
131 ShellError::OnlySupportsThisInputType {
132 exp_input_type: "string and int".into(),
133 wrong_type: other.get_type().to_string(),
134 dst_span: span,
135 src_span: value_span,
136 },
137 span,
138 ),
139 }
140}
141
142fn i64_from_string(a_string: &str, span: Span) -> Result<i64, ShellError> {
143 let locale = get_system_locale();
145
146 let no_comma_string = a_string.replace(locale.separator(), "");
149 let clean_string = no_comma_string.trim();
150
151 if let Some(stripped_negative_string) = clean_string.strip_prefix('-') {
153 match stripped_negative_string.parse::<bytesize::ByteSize>() {
154 Ok(n) => i64_from_byte_size(n, true, span),
155 Err(_) => Err(string_convert_error(span)),
156 }
157 } else if let Some(stripped_positive_string) = clean_string.strip_prefix('+') {
158 match stripped_positive_string.parse::<bytesize::ByteSize>() {
159 Ok(n) if stripped_positive_string.starts_with(|c: char| c.is_ascii_digit()) => {
160 i64_from_byte_size(n, false, span)
161 }
162 _ => Err(string_convert_error(span)),
163 }
164 } else {
165 match clean_string.parse::<bytesize::ByteSize>() {
166 Ok(n) => i64_from_byte_size(n, false, span),
167 Err(_) => Err(string_convert_error(span)),
168 }
169 }
170}
171
172fn i64_from_byte_size(
173 byte_size: bytesize::ByteSize,
174 is_negative: bool,
175 span: Span,
176) -> Result<i64, ShellError> {
177 match i64::try_from(byte_size.as_u64()) {
178 Ok(n) => Ok(if is_negative { -n } else { n }),
179 Err(_) => Err(string_convert_error(span)),
180 }
181}
182
183fn string_convert_error(span: Span) -> ShellError {
184 ShellError::CantConvert {
185 to_type: "filesize".into(),
186 from_type: "string".into(),
187 span,
188 help: None,
189 }
190}
191
192#[cfg(test)]
193mod test {
194 use super::*;
195
196 #[test]
197 fn test_examples() {
198 use crate::test_examples;
199
200 test_examples(IntoFilesize {})
201 }
202}