mirror of
https://github.com/nushell/nushell.git
synced 2025-08-17 16:11:08 +02:00
Rows and values can be checked for emptiness. Allows to set a value if desired. (#1665)
This commit is contained in:
committed by
GitHub
parent
a62745eefb
commit
80025ea684
203
crates/nu-cli/src/commands/is_empty.rs
Normal file
203
crates/nu-cli/src/commands/is_empty.rs
Normal file
@ -0,0 +1,203 @@
|
||||
use crate::commands::PerItemCommand;
|
||||
use crate::context::CommandRegistry;
|
||||
use crate::prelude::*;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{
|
||||
CallInfo, ColumnPath, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value,
|
||||
};
|
||||
use nu_source::Tagged;
|
||||
use nu_value_ext::ValueExt;
|
||||
|
||||
enum IsEmptyFor {
|
||||
Value,
|
||||
RowWithFieldsAndFallback(Vec<Tagged<ColumnPath>>, Value),
|
||||
RowWithField(Tagged<ColumnPath>),
|
||||
RowWithFieldAndFallback(Box<Tagged<ColumnPath>>, Value),
|
||||
}
|
||||
|
||||
pub struct IsEmpty;
|
||||
|
||||
impl PerItemCommand 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."
|
||||
}
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
call_info: &CallInfo,
|
||||
_registry: &CommandRegistry,
|
||||
_raw_args: &RawCommandArgs,
|
||||
value: Value,
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
let value_tag = value.tag();
|
||||
|
||||
let action = if call_info.args.len() <= 2 {
|
||||
let field = call_info.args.expect_nth(0);
|
||||
let replacement_if_true = call_info.args.expect_nth(1);
|
||||
|
||||
match (field, replacement_if_true) {
|
||||
(Ok(field), Ok(replacement_if_true)) => IsEmptyFor::RowWithFieldAndFallback(
|
||||
Box::new(field.as_column_path()?),
|
||||
replacement_if_true.clone(),
|
||||
),
|
||||
(Ok(field), Err(_)) => IsEmptyFor::RowWithField(field.as_column_path()?),
|
||||
(_, _) => IsEmptyFor::Value,
|
||||
}
|
||||
} else {
|
||||
let no_args = vec![];
|
||||
let mut arguments = call_info
|
||||
.args
|
||||
.positional
|
||||
.as_ref()
|
||||
.unwrap_or_else(|| &no_args)
|
||||
.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(futures::stream::iter(vec![Ok(ReturnSuccess::Value(
|
||||
UntaggedValue::boolean(value.is_empty()).into_value(value_tag),
|
||||
))])
|
||||
.to_output_stream()),
|
||||
IsEmptyFor::RowWithFieldsAndFallback(fields, default) => {
|
||||
let mut out = value;
|
||||
|
||||
for field in fields.iter() {
|
||||
let val =
|
||||
out.get_data_by_column_path(&field, Box::new(move |(_, _, err)| err))?;
|
||||
|
||||
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(futures::stream::iter(vec![Ok(ReturnSuccess::Value(out))]).to_output_stream())
|
||||
}
|
||||
IsEmptyFor::RowWithField(field) => {
|
||||
let val =
|
||||
value.get_data_by_column_path(&field, Box::new(move |(_, _, err)| err))?;
|
||||
|
||||
let stream = 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) => futures::stream::iter(vec![Ok(ReturnSuccess::Value(v))]),
|
||||
None => {
|
||||
return Err(ShellError::labeled_error(
|
||||
"empty? could not find place to check emptiness",
|
||||
"column name",
|
||||
&field.tag,
|
||||
))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
futures::stream::iter(vec![Ok(ReturnSuccess::Value(value))])
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
return Err(ShellError::labeled_error(
|
||||
"Unrecognized type in stream",
|
||||
"original value",
|
||||
&value_tag,
|
||||
))
|
||||
}
|
||||
};
|
||||
|
||||
Ok(stream.to_output_stream())
|
||||
}
|
||||
IsEmptyFor::RowWithFieldAndFallback(field, default) => {
|
||||
let val =
|
||||
value.get_data_by_column_path(&field, Box::new(move |(_, _, err)| err))?;
|
||||
|
||||
let stream = match &value {
|
||||
obj
|
||||
@
|
||||
Value {
|
||||
value: UntaggedValue::Row(_),
|
||||
..
|
||||
} => {
|
||||
if val.is_empty() {
|
||||
match obj.replace_data_at_column_path(&field, default) {
|
||||
Some(v) => futures::stream::iter(vec![Ok(ReturnSuccess::Value(v))]),
|
||||
None => {
|
||||
return Err(ShellError::labeled_error(
|
||||
"empty? could not find place to check emptiness",
|
||||
"column name",
|
||||
&field.tag,
|
||||
))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
futures::stream::iter(vec![Ok(ReturnSuccess::Value(value))])
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
return Err(ShellError::labeled_error(
|
||||
"Unrecognized type in stream",
|
||||
"original value",
|
||||
&value_tag,
|
||||
))
|
||||
}
|
||||
};
|
||||
|
||||
Ok(stream.to_output_stream())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user