use crate::commands::WholeStreamCommand; use crate::context::CommandRegistry; use crate::prelude::*; use nu_errors::ShellError; use nu_protocol::{ColumnPath, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value}; use nu_source::Tagged; use nu_value_ext::ValueExt; enum IsEmptyFor { Value, RowWithFieldsAndFallback(Vec>, Value), RowWithField(Tagged), RowWithFieldAndFallback(Box>, Value), } pub struct IsEmpty; #[derive(Deserialize)] pub struct IsEmptyArgs { rest: Vec, } #[async_trait] impl WholeStreamCommand for IsEmpty { fn name(&self) -> &str { "empty?" } fn signature(&self) -> Signature { Signature::build("empty?").rest( SyntaxShape::Any, "the names of the columns to check emptiness followed by the replacement value.", ) } fn usage(&self) -> &str { "Checks emptiness. The last value is the replacement value for any empty column(s) given to check against the table." } async fn run( &self, args: CommandArgs, registry: &CommandRegistry, ) -> Result { is_empty(args, registry).await } } async fn is_empty( args: CommandArgs, registry: &CommandRegistry, ) -> Result { let registry = registry.clone(); let (IsEmptyArgs { rest }, input) = args.process(®istry).await?; Ok(input .map(move |value| { let value_tag = value.tag(); let action = if rest.len() <= 2 { let field = rest.get(0); let replacement_if_true = rest.get(1); match (field, replacement_if_true) { (Some(field), Some(replacement_if_true)) => { IsEmptyFor::RowWithFieldAndFallback( Box::new(field.as_column_path()?), replacement_if_true.clone(), ) } (Some(field), None) => IsEmptyFor::RowWithField(field.as_column_path()?), (_, _) => IsEmptyFor::Value, } } else { // let no_args = vec![]; let mut arguments = rest.iter().rev(); let replacement_if_true = match arguments.next() { Some(arg) => arg.clone(), None => UntaggedValue::boolean(value.is_empty()).into_value(&value_tag), }; IsEmptyFor::RowWithFieldsAndFallback( arguments .map(|a| a.as_column_path()) .filter_map(Result::ok) .collect(), replacement_if_true, ) }; match action { IsEmptyFor::Value => Ok(ReturnSuccess::Value( UntaggedValue::boolean(value.is_empty()).into_value(value_tag), )), IsEmptyFor::RowWithFieldsAndFallback(fields, default) => { let mut out = value; for field in fields.iter() { let val = crate::commands::get::get_column_path(&field, &out)?; let emptiness_value = match out { obj @ Value { value: UntaggedValue::Row(_), .. } => { if val.is_empty() { match obj.replace_data_at_column_path(&field, default.clone()) { Some(v) => Ok(v), None => Err(ShellError::labeled_error( "empty? could not find place to check emptiness", "column name", &field.tag, )), } } else { Ok(obj) } } _ => Err(ShellError::labeled_error( "Unrecognized type in stream", "original value", &value_tag, )), }; out = emptiness_value?; } Ok(ReturnSuccess::Value(out)) } IsEmptyFor::RowWithField(field) => { let val = crate::commands::get::get_column_path(&field, &value)?; match &value { obj @ Value { value: UntaggedValue::Row(_), .. } => { if val.is_empty() { match obj.replace_data_at_column_path( &field, UntaggedValue::boolean(true).into_value(&value_tag), ) { Some(v) => Ok(ReturnSuccess::Value(v)), None => Err(ShellError::labeled_error( "empty? could not find place to check emptiness", "column name", &field.tag, )), } } else { Ok(ReturnSuccess::Value(value)) } } _ => Err(ShellError::labeled_error( "Unrecognized type in stream", "original value", &value_tag, )), } } IsEmptyFor::RowWithFieldAndFallback(field, default) => { let val = crate::commands::get::get_column_path(&field, &value)?; match &value { obj @ Value { value: UntaggedValue::Row(_), .. } => { if val.is_empty() { match obj.replace_data_at_column_path(&field, default) { Some(v) => Ok(ReturnSuccess::Value(v)), None => Err(ShellError::labeled_error( "empty? could not find place to check emptiness", "column name", &field.tag, )), } } else { Ok(ReturnSuccess::Value(value)) } } _ => Err(ShellError::labeled_error( "Unrecognized type in stream", "original value", &value_tag, )), } } } }) .to_output_stream()) } #[cfg(test)] mod tests { use super::IsEmpty; #[test] fn examples_work_as_expected() { use crate::examples::test as test_examples; test_examples(IsEmpty {}) } }