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