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