nu_command/bytes/
split.rs

1use nu_engine::command_prelude::*;
2
3#[derive(Clone)]
4pub struct BytesSplit;
5
6impl Command for BytesSplit {
7    fn name(&self) -> &str {
8        "bytes split"
9    }
10
11    fn signature(&self) -> Signature {
12        Signature::build("bytes split")
13            .input_output_types(vec![(Type::Binary, Type::list(Type::Binary))])
14            .required(
15                "separator",
16                SyntaxShape::OneOf(vec![SyntaxShape::Binary, SyntaxShape::String]),
17                "Bytes or string that the input will be split on (must be non-empty).",
18            )
19            .category(Category::Bytes)
20    }
21
22    fn description(&self) -> &str {
23        "Split input into multiple items using a separator."
24    }
25
26    fn search_terms(&self) -> Vec<&str> {
27        vec!["separate", "stream"]
28    }
29
30    fn examples(&self) -> Vec<Example> {
31        vec![
32            Example {
33                example: r#"0x[66 6F 6F 20 62 61 72 20 62 61 7A 20] | bytes split 0x[20]"#,
34                description: "Split a binary value using a binary separator",
35                result: Some(Value::test_list(vec![
36                    Value::test_binary("foo"),
37                    Value::test_binary("bar"),
38                    Value::test_binary("baz"),
39                    Value::test_binary(""),
40                ])),
41            },
42            Example {
43                example: r#"0x[61 2D 2D 62 2D 2D 63] | bytes split "--""#,
44                description: "Split a binary value using a string separator",
45                result: Some(Value::test_list(vec![
46                    Value::test_binary("a"),
47                    Value::test_binary("b"),
48                    Value::test_binary("c"),
49                ])),
50            },
51        ]
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        let head = call.head;
62        let Spanned {
63            item: separator,
64            span,
65        }: Spanned<Vec<u8>> = call.req(engine_state, stack, 0)?;
66
67        if separator.is_empty() {
68            return Err(ShellError::IncorrectValue {
69                msg: "Separator can't be empty".into(),
70                val_span: span,
71                call_span: call.head,
72            });
73        }
74
75        let (split_read, md) = match input {
76            PipelineData::Value(Value::Binary { val, .. }, md) => (
77                ByteStream::read_binary(val, head, engine_state.signals().clone()).split(separator),
78                md,
79            ),
80            PipelineData::ByteStream(stream, md) => (stream.split(separator), md),
81            input => {
82                let span = input.span().unwrap_or(head);
83                return Err(input.unsupported_input_error("bytes", span));
84            }
85        };
86        if let Some(split) = split_read {
87            Ok(split
88                .map(move |part| match part {
89                    Ok(val) => Value::binary(val, head),
90                    Err(err) => Value::error(err, head),
91                })
92                .into_pipeline_data_with_metadata(head, engine_state.signals().clone(), md))
93        } else {
94            Ok(PipelineData::empty())
95        }
96    }
97}
98
99#[cfg(test)]
100mod test {
101    use super::*;
102
103    #[test]
104    fn test_examples() {
105        use crate::test_examples;
106
107        test_examples(BytesSplit {})
108    }
109}