nu_command/filters/
lines.rs1use 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 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}