nu_command/bytes/
collect.rs

1use itertools::Itertools;
2use nu_engine::command_prelude::*;
3
4#[derive(Clone, Copy)]
5pub struct BytesCollect;
6
7impl Command for BytesCollect {
8    fn name(&self) -> &str {
9        "bytes collect"
10    }
11
12    fn signature(&self) -> Signature {
13        Signature::build("bytes collect")
14            .input_output_types(vec![(Type::List(Box::new(Type::Binary)), Type::Binary)])
15            .optional(
16                "separator",
17                SyntaxShape::Binary,
18                "Optional separator to use when creating binary.",
19            )
20            .category(Category::Bytes)
21    }
22
23    fn description(&self) -> &str {
24        "Concatenate multiple binary into a single binary, with an optional separator between each."
25    }
26
27    fn search_terms(&self) -> Vec<&str> {
28        vec!["join", "concatenate"]
29    }
30
31    fn run(
32        &self,
33        engine_state: &EngineState,
34        stack: &mut Stack,
35        call: &Call,
36        input: PipelineData,
37    ) -> Result<PipelineData, ShellError> {
38        let separator: Option<Vec<u8>> = call.opt(engine_state, stack, 0)?;
39
40        let span = call.head;
41
42        // input should be a list of binary data.
43        let metadata = input.metadata();
44        let iter = Itertools::intersperse(
45            input.into_iter_strict(span)?.map(move |value| {
46                // Everything is wrapped in Some in case there's a separator, so we can flatten
47                Some(match value {
48                    // Explicitly propagate errors instead of dropping them.
49                    Value::Error { error, .. } => Err(*error),
50                    Value::Binary { val, .. } => Ok(val),
51                    other => Err(ShellError::OnlySupportsThisInputType {
52                        exp_input_type: "binary".into(),
53                        wrong_type: other.get_type().to_string(),
54                        dst_span: span,
55                        src_span: other.span(),
56                    }),
57                })
58            }),
59            Ok(separator).transpose(),
60        )
61        .flatten();
62
63        let output = ByteStream::from_result_iter(
64            iter,
65            span,
66            engine_state.signals().clone(),
67            ByteStreamType::Binary,
68        );
69
70        Ok(PipelineData::byte_stream(output, metadata))
71    }
72
73    fn examples(&self) -> Vec<Example> {
74        vec![
75            Example {
76                description: "Create a byte array from input",
77                example: "[0x[11] 0x[13 15]] | bytes collect",
78                result: Some(Value::binary(vec![0x11, 0x13, 0x15], Span::test_data())),
79            },
80            Example {
81                description: "Create a byte array from input with a separator",
82                example: "[0x[11] 0x[33] 0x[44]] | bytes collect 0x[01]",
83                result: Some(Value::binary(
84                    vec![0x11, 0x01, 0x33, 0x01, 0x44],
85                    Span::test_data(),
86                )),
87            },
88        ]
89    }
90}
91
92#[cfg(test)]
93mod tests {
94    use super::*;
95    #[test]
96    fn test_examples() {
97        use crate::test_examples;
98
99        test_examples(BytesCollect {})
100    }
101}