Skip to main content

nu_command/strings/split/
chars.rs

1use crate::{grapheme_flags, grapheme_flags_const};
2use nu_engine::command_prelude::*;
3
4use unicode_segmentation::UnicodeSegmentation;
5
6#[derive(Clone)]
7pub struct SplitChars;
8
9impl Command for SplitChars {
10    fn name(&self) -> &str {
11        "split chars"
12    }
13
14    fn signature(&self) -> Signature {
15        Signature::build("split chars")
16            .input_output_types(vec![
17                (Type::String, Type::List(Box::new(Type::String))),
18                (
19                    Type::List(Box::new(Type::String)),
20                    Type::List(Box::new(Type::List(Box::new(Type::String)))),
21                ),
22            ])
23            .allow_variants_without_examples(true)
24            .switch(
25                "grapheme-clusters",
26                "Split on grapheme clusters.",
27                Some('g'),
28            )
29            .switch(
30                "code-points",
31                "Split on code points (default; splits combined characters).",
32                Some('c'),
33            )
34            .category(Category::Strings)
35    }
36
37    fn description(&self) -> &str {
38        "Split a string into a list of characters."
39    }
40
41    fn search_terms(&self) -> Vec<&str> {
42        vec!["character", "separate", "divide"]
43    }
44
45    fn examples(&self) -> Vec<Example<'_>> {
46        vec![
47            Example {
48                description: "Split the string into a list of characters.",
49                example: "'hello' | split chars",
50                result: Some(Value::list(
51                    vec![
52                        Value::test_string("h"),
53                        Value::test_string("e"),
54                        Value::test_string("l"),
55                        Value::test_string("l"),
56                        Value::test_string("o"),
57                    ],
58                    Span::test_data(),
59                )),
60            },
61            Example {
62                description: "Split on grapheme clusters.",
63                example: "'🇯🇵ほげ' | split chars --grapheme-clusters",
64                result: Some(Value::list(
65                    vec![
66                        Value::test_string("🇯🇵"),
67                        Value::test_string("ほ"),
68                        Value::test_string("げ"),
69                    ],
70                    Span::test_data(),
71                )),
72            },
73            Example {
74                description: "Split multiple strings into lists of characters.",
75                example: "['hello', 'world'] | split chars",
76                result: Some(Value::test_list(vec![
77                    Value::test_list(vec![
78                        Value::test_string("h"),
79                        Value::test_string("e"),
80                        Value::test_string("l"),
81                        Value::test_string("l"),
82                        Value::test_string("o"),
83                    ]),
84                    Value::test_list(vec![
85                        Value::test_string("w"),
86                        Value::test_string("o"),
87                        Value::test_string("r"),
88                        Value::test_string("l"),
89                        Value::test_string("d"),
90                    ]),
91                ])),
92            },
93        ]
94    }
95
96    fn is_const(&self) -> bool {
97        true
98    }
99
100    fn run(
101        &self,
102        engine_state: &EngineState,
103        stack: &mut Stack,
104        call: &Call,
105        input: PipelineData,
106    ) -> Result<PipelineData, ShellError> {
107        let graphemes = grapheme_flags(engine_state, stack, call)?;
108        split_chars(engine_state, call, input, graphemes)
109    }
110
111    fn run_const(
112        &self,
113        working_set: &StateWorkingSet,
114        call: &Call,
115        input: PipelineData,
116    ) -> Result<PipelineData, ShellError> {
117        let graphemes = grapheme_flags_const(working_set, call)?;
118        split_chars(working_set.permanent(), call, input, graphemes)
119    }
120}
121
122fn split_chars(
123    engine_state: &EngineState,
124    call: &Call,
125    input: PipelineData,
126    graphemes: bool,
127) -> Result<PipelineData, ShellError> {
128    let span = call.head;
129    input.map(
130        move |x| split_chars_helper(&x, span, graphemes),
131        engine_state.signals(),
132    )
133}
134
135fn split_chars_helper(v: &Value, name: Span, graphemes: bool) -> Value {
136    let span = v.span();
137    match v {
138        Value::Error { error, .. } => Value::error(*error.clone(), span),
139        v => {
140            let v_span = v.span();
141            if let Ok(s) = v.as_str() {
142                Value::list(
143                    if graphemes {
144                        s.graphemes(true)
145                            .collect::<Vec<_>>()
146                            .into_iter()
147                            .map(move |x| Value::string(x, v_span))
148                            .collect()
149                    } else {
150                        s.chars()
151                            .collect::<Vec<_>>()
152                            .into_iter()
153                            .map(move |x| Value::string(x, v_span))
154                            .collect()
155                    },
156                    v_span,
157                )
158            } else {
159                Value::error(
160                    ShellError::OnlySupportsThisInputType {
161                        exp_input_type: "string".into(),
162                        wrong_type: v.get_type().to_string(),
163                        dst_span: name,
164                        src_span: v_span,
165                    },
166                    name,
167                )
168            }
169        }
170    }
171}
172
173#[cfg(test)]
174mod test {
175    use super::*;
176
177    #[test]
178    fn test_examples() {
179        use crate::test_examples;
180
181        test_examples(SplitChars {})
182    }
183}