1use nu_cmd_base::input_handler::{CmdArgument, operate};
2use nu_engine::command_prelude::*;
3
4struct Arguments {
5 cell_paths: Option<Vec<CellPath>>,
6 compact: bool,
7}
8
9impl CmdArgument for Arguments {
10 fn take_cell_paths(&mut self) -> Option<Vec<CellPath>> {
11 self.cell_paths.take()
12 }
13}
14
15#[derive(Clone)]
16pub struct IntoBinary;
17
18impl Command for IntoBinary {
19 fn name(&self) -> &str {
20 "into binary"
21 }
22
23 fn signature(&self) -> Signature {
24 Signature::build("into binary")
25 .input_output_types(vec![
26 (Type::Binary, Type::Binary),
27 (Type::Int, Type::Binary),
28 (Type::Number, Type::Binary),
29 (Type::String, Type::Binary),
30 (Type::Bool, Type::Binary),
31 (Type::Filesize, Type::Binary),
32 (Type::Date, Type::Binary),
33 (Type::table(), Type::table()),
34 (Type::record(), Type::record()),
35 ])
36 .allow_variants_without_examples(true) .switch("compact", "output without padding zeros", Some('c'))
38 .rest(
39 "rest",
40 SyntaxShape::CellPath,
41 "For a data structure input, convert data at the given cell paths.",
42 )
43 .category(Category::Conversions)
44 }
45
46 fn description(&self) -> &str {
47 "Convert value to a binary primitive."
48 }
49
50 fn search_terms(&self) -> Vec<&str> {
51 vec!["convert", "bytes"]
52 }
53
54 fn run(
55 &self,
56 engine_state: &EngineState,
57 stack: &mut Stack,
58 call: &Call,
59 input: PipelineData,
60 ) -> Result<PipelineData, ShellError> {
61 into_binary(engine_state, stack, call, input)
62 }
63
64 fn examples(&self) -> Vec<Example> {
65 vec![
66 Example {
67 description: "convert string to a nushell binary primitive",
68 example: "'This is a string that is exactly 52 characters long.' | into binary",
69 result: Some(Value::binary(
70 "This is a string that is exactly 52 characters long."
71 .to_string()
72 .as_bytes()
73 .to_vec(),
74 Span::test_data(),
75 )),
76 },
77 Example {
78 description: "convert a number to a nushell binary primitive",
79 example: "1 | into binary",
80 result: Some(Value::binary(
81 i64::from(1).to_ne_bytes().to_vec(),
82 Span::test_data(),
83 )),
84 },
85 Example {
86 description: "convert a boolean to a nushell binary primitive",
87 example: "true | into binary",
88 result: Some(Value::binary(
89 i64::from(1).to_ne_bytes().to_vec(),
90 Span::test_data(),
91 )),
92 },
93 Example {
94 description: "convert a filesize to a nushell binary primitive",
95 example: "ls | where name == LICENSE | get size | into binary",
96 result: None,
97 },
98 Example {
99 description: "convert a filepath to a nushell binary primitive",
100 example: "ls | where name == LICENSE | get name | path expand | into binary",
101 result: None,
102 },
103 Example {
104 description: "convert a float to a nushell binary primitive",
105 example: "1.234 | into binary",
106 result: Some(Value::binary(
107 1.234f64.to_ne_bytes().to_vec(),
108 Span::test_data(),
109 )),
110 },
111 Example {
112 description: "convert an int to a nushell binary primitive with compact enabled",
113 example: "10 | into binary --compact",
114 result: Some(Value::binary(vec![10], Span::test_data())),
115 },
116 ]
117 }
118}
119
120fn into_binary(
121 engine_state: &EngineState,
122 stack: &mut Stack,
123 call: &Call,
124 input: PipelineData,
125) -> Result<PipelineData, ShellError> {
126 let head = call.head;
127 let cell_paths = call.rest(engine_state, stack, 0)?;
128 let cell_paths = (!cell_paths.is_empty()).then_some(cell_paths);
129
130 if let PipelineData::ByteStream(stream, metadata) = input {
131 Ok(PipelineData::ByteStream(
133 stream.with_type(ByteStreamType::Binary),
134 metadata,
135 ))
136 } else {
137 let args = Arguments {
138 cell_paths,
139 compact: call.has_flag(engine_state, stack, "compact")?,
140 };
141 operate(action, args, input, head, engine_state.signals())
142 }
143}
144
145fn action(input: &Value, _args: &Arguments, span: Span) -> Value {
146 let value = match input {
147 Value::Binary { .. } => input.clone(),
148 Value::Int { val, .. } => Value::binary(val.to_ne_bytes().to_vec(), span),
149 Value::Float { val, .. } => Value::binary(val.to_ne_bytes().to_vec(), span),
150 Value::Filesize { val, .. } => Value::binary(val.get().to_ne_bytes().to_vec(), span),
151 Value::String { val, .. } => Value::binary(val.as_bytes().to_vec(), span),
152 Value::Bool { val, .. } => Value::binary(i64::from(*val).to_ne_bytes().to_vec(), span),
153 Value::Duration { val, .. } => Value::binary(val.to_ne_bytes().to_vec(), span),
154 Value::Date { val, .. } => {
155 Value::binary(val.format("%c").to_string().as_bytes().to_vec(), span)
156 }
157 Value::Error { .. } => input.clone(),
159 other => Value::error(
160 ShellError::OnlySupportsThisInputType {
161 exp_input_type: "int, float, filesize, string, date, duration, binary, or bool"
162 .into(),
163 wrong_type: other.get_type().to_string(),
164 dst_span: span,
165 src_span: other.span(),
166 },
167 span,
168 ),
169 };
170
171 if _args.compact {
172 let val_span = value.span();
173 if let Value::Binary { val, .. } = value {
174 let val = if cfg!(target_endian = "little") {
175 match val.iter().rposition(|&x| x != 0) {
176 Some(idx) => &val[..idx + 1],
177
178 None => &[0],
180 }
181 } else {
182 match val.iter().position(|&x| x != 0) {
183 Some(idx) => &val[idx..],
184 None => &[0],
185 }
186 };
187
188 Value::binary(val.to_vec(), val_span)
189 } else {
190 value
191 }
192 } else {
193 value
194 }
195}
196
197#[cfg(test)]
198mod test {
199 use rstest::rstest;
200
201 use super::*;
202
203 #[test]
204 fn test_examples() {
205 use crate::test_examples;
206
207 test_examples(IntoBinary {})
208 }
209
210 #[rstest]
211 #[case(vec![10], vec![10], vec![10])]
212 #[case(vec![10, 0, 0], vec![10], vec![10, 0, 0])]
213 #[case(vec![0, 0, 10], vec![0, 0, 10], vec![10])]
214 #[case(vec![0, 10, 0, 0], vec![0, 10], vec![10, 0, 0])]
215 fn test_compact(#[case] input: Vec<u8>, #[case] little: Vec<u8>, #[case] big: Vec<u8>) {
216 let s = Value::test_binary(input);
217 let actual = action(
218 &s,
219 &Arguments {
220 cell_paths: None,
221 compact: true,
222 },
223 Span::test_data(),
224 );
225 if cfg!(target_endian = "little") {
226 assert_eq!(actual, Value::test_binary(little));
227 } else {
228 assert_eq!(actual, Value::test_binary(big));
229 }
230 }
231}