nu_command/filters/
compact.rs

1use nu_engine::command_prelude::*;
2
3#[derive(Clone)]
4pub struct Compact;
5
6impl Command for Compact {
7    fn name(&self) -> &str {
8        "compact"
9    }
10
11    fn signature(&self) -> Signature {
12        Signature::build("compact")
13            .input_output_types(vec![
14                (Type::record(), Type::record()),
15                (Type::list(Type::Any), Type::list(Type::Any)),
16            ])
17            .switch(
18                "empty",
19                "also compact empty items like \"\", {}, and []",
20                Some('e'),
21            )
22            .rest(
23                "columns",
24                SyntaxShape::Any,
25                "The columns to compact from the table.",
26            )
27            .category(Category::Filters)
28    }
29
30    fn description(&self) -> &str {
31        "Creates a table with non-empty rows."
32    }
33
34    fn extra_description(&self) -> &str {
35        "Can be used to remove `null` or empty values from lists and records too."
36    }
37
38    fn search_terms(&self) -> Vec<&str> {
39        vec!["empty", "remove"]
40    }
41
42    fn run(
43        &self,
44        engine_state: &EngineState,
45        stack: &mut Stack,
46        call: &Call,
47        mut input: PipelineData,
48    ) -> Result<PipelineData, ShellError> {
49        let empty = call.has_flag(engine_state, stack, "empty")?;
50        let columns: Vec<String> = call.rest(engine_state, stack, 0)?;
51
52        match input {
53            PipelineData::Value(Value::Record { ref mut val, .. }, ..) => {
54                val.to_mut().retain(|_, val| do_keep_value(val, empty));
55                Ok(input)
56            }
57            _ => input.filter(
58                move |item| do_keep_row(item, empty, columns.as_slice()),
59                engine_state.signals(),
60            ),
61        }
62    }
63
64    fn examples(&self) -> Vec<Example<'_>> {
65        vec![
66            Example {
67                description: "Filter out all records where 'Hello' is null",
68                example: r#"[["Hello" "World"]; [null 3]] | compact Hello"#,
69                result: Some(Value::test_list(vec![])),
70            },
71            Example {
72                description: "Filter out all records where 'World' is null",
73                example: r#"[["Hello" "World"]; [null 3]] | compact World"#,
74                result: Some(Value::test_list(vec![Value::test_record(record! {
75                    "Hello" => Value::nothing(Span::test_data()),
76                    "World" => Value::test_int(3),
77                })])),
78            },
79            Example {
80                description: "Filter out all instances of null from a list",
81                example: r#"[1, null, 2] | compact"#,
82                result: Some(Value::test_list(vec![
83                    Value::test_int(1),
84                    Value::test_int(2),
85                ])),
86            },
87            Example {
88                description: "Filter out all instances of null and empty items from a list",
89                example: r#"[1, null, 2, "", 3, [], 4, {}, 5] | compact --empty"#,
90                result: Some(Value::test_list(vec![
91                    Value::test_int(1),
92                    Value::test_int(2),
93                    Value::test_int(3),
94                    Value::test_int(4),
95                    Value::test_int(5),
96                ])),
97            },
98            Example {
99                description: "Filter out all instances of null from a record",
100                example: r#"{a: 1, b: null, c: 3} | compact"#,
101                result: Some(Value::test_record(record! {
102                    "a" => Value::test_int(1),
103                    "c" =>  Value::test_int(3),
104                })),
105            },
106        ]
107    }
108}
109
110fn do_keep_value(value: &Value, compact_empties: bool) -> bool {
111    let remove = match compact_empties {
112        true => value.is_empty(),
113        false => value.is_nothing(),
114    };
115    !remove
116}
117
118fn do_keep_row(row: &Value, compact_empties: bool, columns: &[impl AsRef<str>]) -> bool {
119    let do_keep = move |value| do_keep_value(value, compact_empties);
120
121    do_keep(row)
122        && row.as_record().map_or(true, |record| {
123            columns
124                .iter()
125                .all(|col| record.get(col).map(do_keep).unwrap_or(false))
126        })
127}
128
129#[cfg(test)]
130mod tests {
131    use super::Compact;
132
133    #[test]
134    fn examples_work_as_expected() {
135        use crate::test_examples;
136        test_examples(Compact {})
137    }
138}