nu-command 0.112.1

Nushell's built-in commands
Documentation
use nu_engine::command_prelude::*;

#[derive(Clone)]
pub struct Compact;

impl Command for Compact {
    fn name(&self) -> &str {
        "compact"
    }

    fn signature(&self) -> Signature {
        Signature::build("compact")
            .input_output_types(vec![
                (Type::record(), Type::record()),
                (Type::list(Type::Any), Type::list(Type::Any)),
            ])
            .switch(
                "empty",
                "Also compact empty items like \"\", {}, and [].",
                Some('e'),
            )
            .rest(
                "columns",
                SyntaxShape::Any,
                "The columns to compact from the table.",
            )
            .category(Category::Filters)
    }

    fn description(&self) -> &str {
        "Creates a table with non-empty rows."
    }

    fn extra_description(&self) -> &str {
        "Can be used to remove `null` or empty values from lists and records too."
    }

    fn search_terms(&self) -> Vec<&str> {
        vec!["empty", "remove"]
    }

    fn run(
        &self,
        engine_state: &EngineState,
        stack: &mut Stack,
        call: &Call,
        mut input: PipelineData,
    ) -> Result<PipelineData, ShellError> {
        let empty = call.has_flag(engine_state, stack, "empty")?;
        let columns: Vec<String> = call.rest(engine_state, stack, 0)?;

        match input {
            PipelineData::Value(Value::Record { ref mut val, .. }, ..) => {
                val.to_mut().retain(|_, val| do_keep_value(val, empty));
                Ok(input)
            }
            _ => input.filter(
                move |item| do_keep_row(item, empty, columns.as_slice()),
                engine_state.signals(),
            ),
        }
    }

    fn examples(&self) -> Vec<Example<'_>> {
        vec![
            Example {
                description: "Filter out all records where 'Hello' is null",
                example: r#"[["Hello" "World"]; [null 3]] | compact Hello"#,
                result: Some(Value::test_list(vec![])),
            },
            Example {
                description: "Filter out all records where 'World' is null",
                example: r#"[["Hello" "World"]; [null 3]] | compact World"#,
                result: Some(Value::test_list(vec![Value::test_record(record! {
                    "Hello" => Value::nothing(Span::test_data()),
                    "World" => Value::test_int(3),
                })])),
            },
            Example {
                description: "Filter out all instances of null from a list",
                example: "[1, null, 2] | compact",
                result: Some(Value::test_list(vec![
                    Value::test_int(1),
                    Value::test_int(2),
                ])),
            },
            Example {
                description: "Filter out all instances of null and empty items from a list",
                example: r#"[1, null, 2, "", 3, [], 4, {}, 5] | compact --empty"#,
                result: Some(Value::test_list(vec![
                    Value::test_int(1),
                    Value::test_int(2),
                    Value::test_int(3),
                    Value::test_int(4),
                    Value::test_int(5),
                ])),
            },
            Example {
                description: "Filter out all instances of null from a record",
                example: "{a: 1, b: null, c: 3} | compact",
                result: Some(Value::test_record(record! {
                    "a" => Value::test_int(1),
                    "c" =>  Value::test_int(3),
                })),
            },
        ]
    }
}

fn do_keep_value(value: &Value, compact_empties: bool) -> bool {
    let remove = match compact_empties {
        true => value.is_empty(),
        false => value.is_nothing(),
    };
    !remove
}

fn do_keep_row(row: &Value, compact_empties: bool, columns: &[impl AsRef<str>]) -> bool {
    let do_keep = move |value| do_keep_value(value, compact_empties);

    do_keep(row)
        && row.as_record().map_or(true, |record| {
            columns
                .iter()
                .all(|col| record.get(col).map(do_keep).unwrap_or(false))
        })
}

#[cfg(test)]
mod tests {
    use super::Compact;

    #[test]
    fn examples_work_as_expected() -> nu_test_support::Result {
        nu_test_support::test().examples(Compact)
    }
}