nu_command/filters/
lines.rs

1use nu_engine::command_prelude::*;
2
3#[derive(Clone)]
4pub struct Lines;
5
6impl Command for Lines {
7    fn name(&self) -> &str {
8        "lines"
9    }
10
11    fn description(&self) -> &str {
12        "Converts input to lines."
13    }
14
15    fn signature(&self) -> nu_protocol::Signature {
16        Signature::build("lines")
17            .input_output_types(vec![(Type::Any, Type::List(Box::new(Type::String)))])
18            .switch("skip-empty", "skip empty lines", Some('s'))
19            .category(Category::Filters)
20    }
21    fn run(
22        &self,
23        engine_state: &EngineState,
24        stack: &mut Stack,
25        call: &Call,
26        input: PipelineData,
27    ) -> Result<PipelineData, ShellError> {
28        let head = call.head;
29        let skip_empty = call.has_flag(engine_state, stack, "skip-empty")?;
30
31        let span = input.span().unwrap_or(call.head);
32        match input {
33            PipelineData::Value(value, ..) => match value {
34                Value::String { val, .. } => {
35                    let lines = if skip_empty {
36                        val.lines()
37                            .filter_map(|s| {
38                                if s.trim().is_empty() {
39                                    None
40                                } else {
41                                    Some(Value::string(s, span))
42                                }
43                            })
44                            .collect()
45                    } else {
46                        val.lines().map(|s| Value::string(s, span)).collect()
47                    };
48
49                    Ok(Value::list(lines, span).into_pipeline_data())
50                }
51                // Propagate existing errors
52                Value::Error { error, .. } => Err(*error),
53                value => Err(ShellError::OnlySupportsThisInputType {
54                    exp_input_type: "string or byte stream".into(),
55                    wrong_type: value.get_type().to_string(),
56                    dst_span: head,
57                    src_span: value.span(),
58                }),
59            },
60            PipelineData::Empty => Ok(PipelineData::empty()),
61            PipelineData::ListStream(stream, metadata) => {
62                let stream = stream.modify(|iter| {
63                    iter.filter_map(move |value| {
64                        let span = value.span();
65                        if let Value::String { val, .. } = value {
66                            Some(
67                                val.lines()
68                                    .filter_map(|s| {
69                                        if skip_empty && s.trim().is_empty() {
70                                            None
71                                        } else {
72                                            Some(Value::string(s, span))
73                                        }
74                                    })
75                                    .collect::<Vec<_>>(),
76                            )
77                        } else {
78                            None
79                        }
80                    })
81                    .flatten()
82                });
83
84                Ok(PipelineData::list_stream(stream, metadata))
85            }
86            PipelineData::ByteStream(stream, ..) => {
87                if let Some(lines) = stream.lines() {
88                    Ok(lines
89                        .map(move |line| match line {
90                            Ok(line) => Value::string(line, head),
91                            Err(err) => Value::error(err, head),
92                        })
93                        .into_pipeline_data(head, engine_state.signals().clone()))
94                } else {
95                    Ok(PipelineData::empty())
96                }
97            }
98        }
99    }
100
101    fn examples(&self) -> Vec<Example<'_>> {
102        vec![Example {
103            description: "Split multi-line string into lines",
104            example: r#"$"two\nlines" | lines"#,
105            result: Some(Value::list(
106                vec![Value::test_string("two"), Value::test_string("lines")],
107                Span::test_data(),
108            )),
109        }]
110    }
111}
112
113#[cfg(test)]
114mod test {
115    use super::*;
116
117    #[test]
118    fn test_examples() {
119        use crate::test_examples;
120
121        test_examples(Lines {})
122    }
123}