mirror of
https://github.com/nushell/nushell
synced 2024-12-26 13:03:07 +00:00
Rows and values can be checked for emptiness. Allows to set a value if desired. (#1665)
This commit is contained in:
parent
a62745eefb
commit
80025ea684
11 changed files with 437 additions and 98 deletions
|
@ -307,6 +307,7 @@ pub fn create_default_context(
|
||||||
whole_stream_command(Rename),
|
whole_stream_command(Rename),
|
||||||
whole_stream_command(Uniq),
|
whole_stream_command(Uniq),
|
||||||
per_item_command(Each),
|
per_item_command(Each),
|
||||||
|
per_item_command(IsEmpty),
|
||||||
// Table manipulation
|
// Table manipulation
|
||||||
whole_stream_command(Shuffle),
|
whole_stream_command(Shuffle),
|
||||||
whole_stream_command(Wrap),
|
whole_stream_command(Wrap),
|
||||||
|
|
|
@ -54,6 +54,7 @@ pub(crate) mod help;
|
||||||
pub(crate) mod histogram;
|
pub(crate) mod histogram;
|
||||||
pub(crate) mod history;
|
pub(crate) mod history;
|
||||||
pub(crate) mod insert;
|
pub(crate) mod insert;
|
||||||
|
pub(crate) mod is_empty;
|
||||||
pub(crate) mod last;
|
pub(crate) mod last;
|
||||||
pub(crate) mod lines;
|
pub(crate) mod lines;
|
||||||
pub(crate) mod ls;
|
pub(crate) mod ls;
|
||||||
|
@ -135,6 +136,7 @@ pub(crate) use du::Du;
|
||||||
pub(crate) use each::Each;
|
pub(crate) use each::Each;
|
||||||
pub(crate) use echo::Echo;
|
pub(crate) use echo::Echo;
|
||||||
pub(crate) use edit::Edit;
|
pub(crate) use edit::Edit;
|
||||||
|
pub(crate) use is_empty::IsEmpty;
|
||||||
pub(crate) mod kill;
|
pub(crate) mod kill;
|
||||||
pub(crate) use kill::Kill;
|
pub(crate) use kill::Kill;
|
||||||
pub(crate) mod clear;
|
pub(crate) mod clear;
|
||||||
|
|
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())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
77
crates/nu-cli/src/env/environment_syncer.rs
vendored
77
crates/nu-cli/src/env/environment_syncer.rs
vendored
|
@ -58,7 +58,7 @@ impl EnvironmentSyncer {
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(variables) = environment.env() {
|
if let Some(variables) = environment.env() {
|
||||||
for var in nu_value_ext::row_entries(&variables) {
|
for var in variables.row_entries() {
|
||||||
if let Ok(string) = var.1.as_string() {
|
if let Ok(string) = var.1.as_string() {
|
||||||
ctx.with_host(|host| {
|
ctx.with_host(|host| {
|
||||||
host.env_set(
|
host.env_set(
|
||||||
|
@ -88,7 +88,8 @@ impl EnvironmentSyncer {
|
||||||
|
|
||||||
if let Some(new_paths) = environment.path() {
|
if let Some(new_paths) = environment.path() {
|
||||||
let prepared = std::env::join_paths(
|
let prepared = std::env::join_paths(
|
||||||
nu_value_ext::table_entries(&new_paths)
|
new_paths
|
||||||
|
.table_entries()
|
||||||
.map(|p| p.as_string())
|
.map(|p| p.as_string())
|
||||||
.filter_map(Result::ok),
|
.filter_map(Result::ok),
|
||||||
);
|
);
|
||||||
|
@ -212,16 +213,17 @@ mod tests {
|
||||||
// including the newer one accounted for.
|
// including the newer one accounted for.
|
||||||
let environment = actual.env.lock();
|
let environment = actual.env.lock();
|
||||||
|
|
||||||
let vars = nu_value_ext::row_entries(
|
let vars = environment
|
||||||
&environment.env().expect("No variables in the environment."),
|
.env()
|
||||||
)
|
.expect("No variables in the environment.")
|
||||||
.map(|(name, value)| {
|
.row_entries()
|
||||||
(
|
.map(|(name, value)| {
|
||||||
name.to_string(),
|
(
|
||||||
value.as_string().expect("Couldn't convert to string"),
|
name.to_string(),
|
||||||
)
|
value.as_string().expect("Couldn't convert to string"),
|
||||||
})
|
)
|
||||||
.collect::<Vec<_>>();
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
assert_eq!(vars, expected);
|
assert_eq!(vars, expected);
|
||||||
});
|
});
|
||||||
|
@ -281,16 +283,17 @@ mod tests {
|
||||||
|
|
||||||
let environment = actual.env.lock();
|
let environment = actual.env.lock();
|
||||||
|
|
||||||
let vars = nu_value_ext::row_entries(
|
let vars = environment
|
||||||
&environment.env().expect("No variables in the environment."),
|
.env()
|
||||||
)
|
.expect("No variables in the environment.")
|
||||||
.map(|(name, value)| {
|
.row_entries()
|
||||||
(
|
.map(|(name, value)| {
|
||||||
name.to_string(),
|
(
|
||||||
value.as_string().expect("Couldn't convert to string"),
|
name.to_string(),
|
||||||
)
|
value.as_string().expect("Couldn't convert to string"),
|
||||||
})
|
)
|
||||||
.collect::<Vec<_>>();
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
assert_eq!(vars, expected);
|
assert_eq!(vars, expected);
|
||||||
});
|
});
|
||||||
|
@ -367,14 +370,13 @@ mod tests {
|
||||||
let environment = actual.env.lock();
|
let environment = actual.env.lock();
|
||||||
|
|
||||||
let paths = std::env::join_paths(
|
let paths = std::env::join_paths(
|
||||||
&nu_value_ext::table_entries(
|
&environment
|
||||||
&environment
|
.path()
|
||||||
.path()
|
.expect("No path variable in the environment.")
|
||||||
.expect("No path variable in the environment."),
|
.table_entries()
|
||||||
)
|
.map(|value| value.as_string().expect("Couldn't convert to string"))
|
||||||
.map(|value| value.as_string().expect("Couldn't convert to string"))
|
.map(PathBuf::from)
|
||||||
.map(PathBuf::from)
|
.collect::<Vec<_>>(),
|
||||||
.collect::<Vec<_>>(),
|
|
||||||
)
|
)
|
||||||
.expect("Couldn't join paths.")
|
.expect("Couldn't join paths.")
|
||||||
.into_string()
|
.into_string()
|
||||||
|
@ -442,14 +444,13 @@ mod tests {
|
||||||
let environment = actual.env.lock();
|
let environment = actual.env.lock();
|
||||||
|
|
||||||
let paths = std::env::join_paths(
|
let paths = std::env::join_paths(
|
||||||
&nu_value_ext::table_entries(
|
&environment
|
||||||
&environment
|
.path()
|
||||||
.path()
|
.expect("No path variable in the environment.")
|
||||||
.expect("No path variable in the environment."),
|
.table_entries()
|
||||||
)
|
.map(|value| value.as_string().expect("Couldn't convert to string"))
|
||||||
.map(|value| value.as_string().expect("Couldn't convert to string"))
|
.map(PathBuf::from)
|
||||||
.map(PathBuf::from)
|
.collect::<Vec<_>>(),
|
||||||
.collect::<Vec<_>>(),
|
|
||||||
)
|
)
|
||||||
.expect("Couldn't join paths.")
|
.expect("Couldn't join paths.")
|
||||||
.into_string()
|
.into_string()
|
||||||
|
|
|
@ -69,7 +69,7 @@ impl ValueStructure {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build(&mut self, src: &Value, lvl: usize) -> Result<(), ShellError> {
|
fn build(&mut self, src: &Value, lvl: usize) -> Result<(), ShellError> {
|
||||||
for entry in nu_value_ext::row_entries(src) {
|
for entry in src.row_entries() {
|
||||||
let value = entry.1;
|
let value = entry.1;
|
||||||
let path = entry.0;
|
let path = entry.0;
|
||||||
|
|
||||||
|
|
96
crates/nu-cli/tests/commands/is_empty.rs
Normal file
96
crates/nu-cli/tests/commands/is_empty.rs
Normal file
|
@ -0,0 +1,96 @@
|
||||||
|
use nu_test_support::fs::Stub::FileWithContentToBeTrimmed;
|
||||||
|
use nu_test_support::playground::Playground;
|
||||||
|
use nu_test_support::{nu, pipeline};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn adds_value_provided_if_column_is_empty() {
|
||||||
|
Playground::setup("is_empty_test_1", |dirs, sandbox| {
|
||||||
|
sandbox.with_files(vec![FileWithContentToBeTrimmed(
|
||||||
|
"likes.csv",
|
||||||
|
r#"
|
||||||
|
first_name,last_name,rusty_at,likes
|
||||||
|
Andrés,Robalino,10/11/2013,1
|
||||||
|
Jonathan,Turner,10/12/2013,1
|
||||||
|
Jason,Gedge,10/11/2013,1
|
||||||
|
Yehuda,Katz,10/11/2013,
|
||||||
|
"#,
|
||||||
|
)]);
|
||||||
|
|
||||||
|
let actual = nu!(
|
||||||
|
cwd: dirs.test(), pipeline(
|
||||||
|
r#"
|
||||||
|
open likes.csv
|
||||||
|
| empty? likes 1
|
||||||
|
| get likes
|
||||||
|
| sum
|
||||||
|
| echo $it
|
||||||
|
"#
|
||||||
|
));
|
||||||
|
|
||||||
|
assert_eq!(actual, "4");
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn adds_value_provided_for_columns_that_are_empty() {
|
||||||
|
Playground::setup("is_empty_test_2", |dirs, sandbox| {
|
||||||
|
sandbox.with_files(vec![FileWithContentToBeTrimmed(
|
||||||
|
"checks.json",
|
||||||
|
r#"
|
||||||
|
[
|
||||||
|
{"boost": 1, "check": []},
|
||||||
|
{"boost": 1, "check": ""},
|
||||||
|
{"boost": 1, "check": {}},
|
||||||
|
{"boost": null, "check": ["" {} [] ""]}
|
||||||
|
]
|
||||||
|
|
||||||
|
"#,
|
||||||
|
)]);
|
||||||
|
|
||||||
|
let actual = nu!(
|
||||||
|
cwd: dirs.test(), pipeline(
|
||||||
|
r#"
|
||||||
|
open checks.json
|
||||||
|
| empty? boost check 1
|
||||||
|
| get boost check
|
||||||
|
| sum
|
||||||
|
| echo $it
|
||||||
|
"#
|
||||||
|
));
|
||||||
|
|
||||||
|
assert_eq!(actual, "8");
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn value_emptiness_check() {
|
||||||
|
Playground::setup("is_empty_test_3", |dirs, sandbox| {
|
||||||
|
sandbox.with_files(vec![FileWithContentToBeTrimmed(
|
||||||
|
"checks.json",
|
||||||
|
r#"
|
||||||
|
{
|
||||||
|
"are_empty": [
|
||||||
|
{"check": []},
|
||||||
|
{"check": ""},
|
||||||
|
{"check": {}},
|
||||||
|
{"check": ["" {} [] ""]}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
)]);
|
||||||
|
|
||||||
|
let actual = nu!(
|
||||||
|
cwd: dirs.test(), pipeline(
|
||||||
|
r#"
|
||||||
|
open checks.json
|
||||||
|
| get are_empty.check
|
||||||
|
| empty?
|
||||||
|
| where $it
|
||||||
|
| count
|
||||||
|
| echo $it
|
||||||
|
"#
|
||||||
|
));
|
||||||
|
|
||||||
|
assert_eq!(actual, "4");
|
||||||
|
})
|
||||||
|
}
|
|
@ -16,6 +16,7 @@ mod group_by;
|
||||||
mod headers;
|
mod headers;
|
||||||
mod histogram;
|
mod histogram;
|
||||||
mod insert;
|
mod insert;
|
||||||
|
mod is_empty;
|
||||||
mod last;
|
mod last;
|
||||||
mod lines;
|
mod lines;
|
||||||
mod ls;
|
mod ls;
|
||||||
|
|
|
@ -3,6 +3,7 @@ mod convert;
|
||||||
mod debug;
|
mod debug;
|
||||||
pub mod dict;
|
pub mod dict;
|
||||||
pub mod evaluate;
|
pub mod evaluate;
|
||||||
|
pub mod iter;
|
||||||
pub mod primitive;
|
pub mod primitive;
|
||||||
pub mod range;
|
pub mod range;
|
||||||
mod serde_bigdecimal;
|
mod serde_bigdecimal;
|
||||||
|
@ -11,6 +12,7 @@ mod serde_bigint;
|
||||||
use crate::hir;
|
use crate::hir;
|
||||||
use crate::type_name::{ShellTypeName, SpannedTypeName};
|
use crate::type_name::{ShellTypeName, SpannedTypeName};
|
||||||
use crate::value::dict::Dictionary;
|
use crate::value::dict::Dictionary;
|
||||||
|
use crate::value::iter::{RowValueIter, TableValueIter};
|
||||||
use crate::value::primitive::Primitive;
|
use crate::value::primitive::Primitive;
|
||||||
use crate::value::range::{Range, RangeInclusion};
|
use crate::value::range::{Range, RangeInclusion};
|
||||||
use crate::{ColumnPath, PathMember};
|
use crate::{ColumnPath, PathMember};
|
||||||
|
@ -313,6 +315,39 @@ impl Value {
|
||||||
_ => Err(ShellError::type_error("boolean", self.spanned_type_name())),
|
_ => Err(ShellError::type_error("boolean", self.spanned_type_name())),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns an iterator of the values rows
|
||||||
|
pub fn table_entries(&self) -> TableValueIter<'_> {
|
||||||
|
crate::value::iter::table_entries(&self)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns an iterator of the value's cells
|
||||||
|
pub fn row_entries(&self) -> RowValueIter<'_> {
|
||||||
|
crate::value::iter::row_entries(&self)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns true if the value is empty
|
||||||
|
pub fn is_empty(&self) -> bool {
|
||||||
|
match &self {
|
||||||
|
Value {
|
||||||
|
value: UntaggedValue::Primitive(p),
|
||||||
|
..
|
||||||
|
} => p.is_empty(),
|
||||||
|
t
|
||||||
|
@
|
||||||
|
Value {
|
||||||
|
value: UntaggedValue::Table(_),
|
||||||
|
..
|
||||||
|
} => t.table_entries().all(|row| row.is_empty()),
|
||||||
|
r
|
||||||
|
@
|
||||||
|
Value {
|
||||||
|
value: UntaggedValue::Row(_),
|
||||||
|
..
|
||||||
|
} => r.row_entries().all(|(_, value)| value.is_empty()),
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Into<Value> for String {
|
impl Into<Value> for String {
|
||||||
|
|
50
crates/nu-protocol/src/value/iter.rs
Normal file
50
crates/nu-protocol/src/value/iter.rs
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
use crate::value::{UntaggedValue, Value};
|
||||||
|
|
||||||
|
pub enum RowValueIter<'a> {
|
||||||
|
Empty,
|
||||||
|
Entries(indexmap::map::Iter<'a, String, Value>),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum TableValueIter<'a> {
|
||||||
|
Empty,
|
||||||
|
Entries(std::slice::Iter<'a, Value>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Iterator for RowValueIter<'a> {
|
||||||
|
type Item = (&'a String, &'a Value);
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
match self {
|
||||||
|
RowValueIter::Empty => None,
|
||||||
|
RowValueIter::Entries(iter) => iter.next(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Iterator for TableValueIter<'a> {
|
||||||
|
type Item = &'a Value;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
match self {
|
||||||
|
TableValueIter::Empty => None,
|
||||||
|
TableValueIter::Entries(iter) => iter.next(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn table_entries(value: &Value) -> TableValueIter<'_> {
|
||||||
|
match &value.value {
|
||||||
|
UntaggedValue::Table(t) => TableValueIter::Entries(t.iter()),
|
||||||
|
_ => TableValueIter::Empty,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn row_entries(value: &Value) -> RowValueIter<'_> {
|
||||||
|
match &value.value {
|
||||||
|
UntaggedValue::Row(o) => {
|
||||||
|
let iter = o.entries.iter();
|
||||||
|
RowValueIter::Entries(iter)
|
||||||
|
}
|
||||||
|
_ => RowValueIter::Empty,
|
||||||
|
}
|
||||||
|
}
|
|
@ -84,6 +84,15 @@ impl Primitive {
|
||||||
)),
|
)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns true if the value is empty
|
||||||
|
pub fn is_empty(&self) -> bool {
|
||||||
|
match self {
|
||||||
|
Primitive::Nothing => true,
|
||||||
|
Primitive::String(s) => s.is_empty(),
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl num_traits::Zero for Primitive {
|
impl num_traits::Zero for Primitive {
|
||||||
|
|
|
@ -8,8 +8,6 @@ use nu_source::{HasSpan, PrettyDebug, Spanned, SpannedItem, Tag, Tagged, TaggedI
|
||||||
use num_traits::cast::ToPrimitive;
|
use num_traits::cast::ToPrimitive;
|
||||||
|
|
||||||
pub trait ValueExt {
|
pub trait ValueExt {
|
||||||
fn row_entries(&self) -> RowValueIter<'_>;
|
|
||||||
fn table_entries(&self) -> TableValueIter<'_>;
|
|
||||||
fn into_parts(self) -> (UntaggedValue, Tag);
|
fn into_parts(self) -> (UntaggedValue, Tag);
|
||||||
fn get_data(&self, desc: &str) -> MaybeOwned<'_, Value>;
|
fn get_data(&self, desc: &str) -> MaybeOwned<'_, Value>;
|
||||||
fn get_data_by_key(&self, name: Spanned<&str>) -> Option<Value>;
|
fn get_data_by_key(&self, name: Spanned<&str>) -> Option<Value>;
|
||||||
|
@ -41,14 +39,6 @@ pub trait ValueExt {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ValueExt for Value {
|
impl ValueExt for Value {
|
||||||
fn row_entries(&self) -> RowValueIter<'_> {
|
|
||||||
row_entries(self)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn table_entries(&self) -> TableValueIter<'_> {
|
|
||||||
table_entries(self)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn into_parts(self) -> (UntaggedValue, Tag) {
|
fn into_parts(self) -> (UntaggedValue, Tag) {
|
||||||
(self.value, self.tag)
|
(self.value, self.tag)
|
||||||
}
|
}
|
||||||
|
@ -534,52 +524,3 @@ pub(crate) fn get_mut_data_by_member<'value>(
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum RowValueIter<'a> {
|
|
||||||
Empty,
|
|
||||||
Entries(indexmap::map::Iter<'a, String, Value>),
|
|
||||||
}
|
|
||||||
|
|
||||||
pub enum TableValueIter<'a> {
|
|
||||||
Empty,
|
|
||||||
Entries(std::slice::Iter<'a, Value>),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> Iterator for RowValueIter<'a> {
|
|
||||||
type Item = (&'a String, &'a Value);
|
|
||||||
|
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
|
||||||
match self {
|
|
||||||
RowValueIter::Empty => None,
|
|
||||||
RowValueIter::Entries(iter) => iter.next(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> Iterator for TableValueIter<'a> {
|
|
||||||
type Item = &'a Value;
|
|
||||||
|
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
|
||||||
match self {
|
|
||||||
TableValueIter::Empty => None,
|
|
||||||
TableValueIter::Entries(iter) => iter.next(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn table_entries(value: &Value) -> TableValueIter<'_> {
|
|
||||||
match &value.value {
|
|
||||||
UntaggedValue::Table(t) => TableValueIter::Entries(t.iter()),
|
|
||||||
_ => TableValueIter::Empty,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn row_entries(value: &Value) -> RowValueIter<'_> {
|
|
||||||
match &value.value {
|
|
||||||
UntaggedValue::Row(o) => {
|
|
||||||
let iter = o.entries.iter();
|
|
||||||
RowValueIter::Entries(iter)
|
|
||||||
}
|
|
||||||
_ => RowValueIter::Empty,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
Loading…
Reference in a new issue