Skip to main content

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