diff --git a/crates/nu-value-ext/src/lib.rs b/crates/nu-value-ext/src/lib.rs index 990cc3b124..c575c2f0b1 100644 --- a/crates/nu-value-ext/src/lib.rs +++ b/crates/nu-value-ext/src/lib.rs @@ -104,7 +104,7 @@ impl ValueExt for Value { } } -pub(crate) fn get_data_by_member(value: &Value, name: &PathMember) -> Result { +pub fn get_data_by_member(value: &Value, name: &PathMember) -> Result { match &value.value { // If the value is a row, the member is a column name UntaggedValue::Row(o) => match &name.unspanned { @@ -453,7 +453,7 @@ pub(crate) fn get_data_by_index(value: &Value, idx: Spanned) -> Option) -> Option { +pub fn get_data_by_key(value: &Value, name: Spanned<&str>) -> Option { match &value.value { UntaggedValue::Row(o) => o.get_data_by_key(name), UntaggedValue::Table(l) => { diff --git a/src/commands/default.rs b/src/commands/default.rs index c423f06b2b..52ab166d5e 100644 --- a/src/commands/default.rs +++ b/src/commands/default.rs @@ -4,6 +4,7 @@ use crate::prelude::*; use nu_errors::ShellError; use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value}; use nu_source::Tagged; +use nu_value_ext::ValueExt; #[derive(Deserialize)] struct DefaultArgs { diff --git a/src/commands/edit.rs b/src/commands/edit.rs index 6a649599c7..48d9f0669d 100644 --- a/src/commands/edit.rs +++ b/src/commands/edit.rs @@ -3,6 +3,7 @@ use crate::context::CommandRegistry; use crate::prelude::*; use nu_errors::ShellError; use nu_protocol::{CallInfo, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value}; +use nu_value_ext::ValueExt; pub struct Edit; diff --git a/src/commands/evaluate_by.rs b/src/commands/evaluate_by.rs index 79a06ee1bc..13504361b5 100644 --- a/src/commands/evaluate_by.rs +++ b/src/commands/evaluate_by.rs @@ -3,6 +3,7 @@ use crate::prelude::*; use nu_errors::ShellError; use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value}; use nu_source::{SpannedItem, Tagged}; +use nu_value_ext::ValueExt; pub struct EvaluateBy; diff --git a/src/commands/get.rs b/src/commands/get.rs index d1f242169f..ddc1ec2382 100644 --- a/src/commands/get.rs +++ b/src/commands/get.rs @@ -1,5 +1,4 @@ use crate::commands::WholeStreamCommand; -use crate::data::base::property_get::get_data_by_column_path; use crate::data::base::shape::Shapes; use crate::prelude::*; use futures_util::pin_mut; @@ -10,6 +9,7 @@ use nu_protocol::{ Value, }; use nu_source::{span_for_spanned_list, PrettyDebug}; +use nu_value_ext::get_data_by_column_path; pub struct Get; diff --git a/src/commands/group_by.rs b/src/commands/group_by.rs index 55afb1fe54..c53cf35071 100644 --- a/src/commands/group_by.rs +++ b/src/commands/group_by.rs @@ -1,9 +1,9 @@ use crate::commands::WholeStreamCommand; -use crate::data::base::property_get::get_data_by_key; use crate::prelude::*; use nu_errors::ShellError; use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, TaggedDictBuilder, UntaggedValue, Value}; use nu_source::Tagged; +use nu_value_ext::get_data_by_key; pub struct GroupBy; diff --git a/src/commands/help.rs b/src/commands/help.rs index 132c4fc771..f1da71284b 100644 --- a/src/commands/help.rs +++ b/src/commands/help.rs @@ -1,5 +1,4 @@ use crate::commands::PerItemCommand; -use crate::data::base::property_get::get_data_by_key; use crate::data::command_dict; use crate::prelude::*; use nu_errors::ShellError; @@ -8,6 +7,7 @@ use nu_protocol::{ TaggedDictBuilder, UntaggedValue, Value, }; use nu_source::SpannedItem; +use nu_value_ext::get_data_by_key; pub struct Help; diff --git a/src/commands/insert.rs b/src/commands/insert.rs index dc683c1913..05dd661947 100644 --- a/src/commands/insert.rs +++ b/src/commands/insert.rs @@ -3,6 +3,7 @@ use crate::context::CommandRegistry; use crate::prelude::*; use nu_errors::ShellError; use nu_protocol::{CallInfo, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value}; +use nu_value_ext::ValueExt; pub struct Insert; diff --git a/src/commands/pivot.rs b/src/commands/pivot.rs index a1cff062e7..810b977200 100644 --- a/src/commands/pivot.rs +++ b/src/commands/pivot.rs @@ -1,9 +1,9 @@ use crate::commands::WholeStreamCommand; -use crate::data::base::property_get::get_data_by_key; use crate::prelude::*; use nu_errors::ShellError; use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, TaggedDictBuilder, UntaggedValue, Value}; use nu_source::{SpannedItem, Tagged}; +use nu_value_ext::get_data_by_key; pub struct Pivot; diff --git a/src/commands/sort_by.rs b/src/commands/sort_by.rs index ed4180ef30..f11ff36927 100644 --- a/src/commands/sort_by.rs +++ b/src/commands/sort_by.rs @@ -1,9 +1,9 @@ use crate::commands::WholeStreamCommand; -use crate::data::base::property_get::get_data_by_key; use crate::prelude::*; use nu_errors::ShellError; use nu_protocol::{Signature, SyntaxShape, Value}; use nu_source::Tagged; +use nu_value_ext::get_data_by_key; pub struct SortBy; diff --git a/src/commands/t_sort_by.rs b/src/commands/t_sort_by.rs index cd96cc9be1..447dd4f605 100644 --- a/src/commands/t_sort_by.rs +++ b/src/commands/t_sort_by.rs @@ -1,5 +1,4 @@ use crate::commands::WholeStreamCommand; -use crate::data::base::property_get::get_data_by_key; use crate::data::TaggedListBuilder; use crate::prelude::*; use chrono::{DateTime, NaiveDate, Utc}; @@ -8,6 +7,7 @@ use nu_protocol::{ Primitive, ReturnSuccess, Signature, SyntaxShape, TaggedDictBuilder, UntaggedValue, Value, }; use nu_source::Tagged; +use nu_value_ext::get_data_by_key; pub struct TSortBy; diff --git a/src/commands/to_delimited_data.rs b/src/commands/to_delimited_data.rs index 053ef60d34..e54553ae91 100644 --- a/src/commands/to_delimited_data.rs +++ b/src/commands/to_delimited_data.rs @@ -1,10 +1,10 @@ -use crate::data::base::property_get::get_data_by_key; use crate::prelude::*; use csv::WriterBuilder; use indexmap::{indexset, IndexSet}; use nu_errors::ShellError; use nu_protocol::{Primitive, ReturnSuccess, UntaggedValue, Value}; use nu_source::Spanned; +use nu_value_ext::get_data_by_key; fn from_value_to_delimited_string( tagged_value: &Value, diff --git a/src/data/base.rs b/src/data/base.rs index 7f3a7c0557..f30397a956 100644 --- a/src/data/base.rs +++ b/src/data/base.rs @@ -1,8 +1,6 @@ -pub(crate) mod property_get; pub(crate) mod shape; use crate::context::CommandRegistry; -use crate::data::base::property_get::ValueExt; use crate::evaluate::evaluate_baseline_expr; use bigdecimal::BigDecimal; use chrono::{DateTime, Utc}; @@ -15,6 +13,7 @@ use nu_protocol::{ UntaggedValue, Value, }; use nu_source::{Tag, Text}; +use nu_value_ext::ValueExt; use num_bigint::BigInt; use num_traits::Zero; use query_interface::{interfaces, vtable_for, ObjectHash}; @@ -196,11 +195,11 @@ fn coerce_compare_primitive( } #[cfg(test)] mod tests { - use crate::data::base::property_get::{as_column_path, ValueExt}; use indexmap::IndexMap; use nu_errors::ShellError; use nu_protocol::{ColumnPath as ColumnPathValue, PathMember, UntaggedValue, Value}; use nu_source::*; + use nu_value_ext::{as_column_path, ValueExt}; use num_bigint::BigInt; fn string(input: impl Into) -> Value { diff --git a/src/data/base/property_get.rs b/src/data/base/property_get.rs deleted file mode 100644 index bc0d3b4b09..0000000000 --- a/src/data/base/property_get.rs +++ /dev/null @@ -1,514 +0,0 @@ -use crate::prelude::*; -use nu_errors::{ExpectedRange, ShellError}; -use nu_protocol::{ - ColumnPath, PathMember, Primitive, ShellTypeName, SpannedTypeName, UnspannedPathMember, - UntaggedValue, Value, -}; -use nu_source::{Spanned, SpannedItem, Tagged}; - -pub trait ValueExt { - fn into_parts(self) -> (UntaggedValue, Tag); - fn get_data(&self, desc: &String) -> MaybeOwned<'_, Value>; - fn get_data_by_key(&self, name: Spanned<&str>) -> Option; - fn get_data_by_member(&self, name: &PathMember) -> Result; - fn get_data_by_column_path( - &self, - path: &ColumnPath, - callback: Box ShellError>, - ) -> Result; - fn insert_data_at_path(&self, path: &str, new_value: Value) -> Option; - fn insert_data_at_member( - &mut self, - member: &PathMember, - new_value: Value, - ) -> Result<(), ShellError>; - fn insert_data_at_column_path( - &self, - split_path: &ColumnPath, - new_value: Value, - ) -> Result; - fn replace_data_at_column_path( - &self, - split_path: &ColumnPath, - replaced_value: Value, - ) -> Option; - fn as_column_path(&self) -> Result, ShellError>; - fn as_path_member(&self) -> Result; - fn as_string(&self) -> Result; -} - -impl ValueExt for Value { - fn into_parts(self) -> (UntaggedValue, Tag) { - (self.value, self.tag) - } - - fn get_data(&self, desc: &String) -> MaybeOwned<'_, Value> { - get_data(self, desc) - } - - fn get_data_by_key(&self, name: Spanned<&str>) -> Option { - get_data_by_key(self, name) - } - - fn get_data_by_member(&self, name: &PathMember) -> Result { - get_data_by_member(self, name) - } - - fn get_data_by_column_path( - &self, - path: &ColumnPath, - callback: Box ShellError>, - ) -> Result { - get_data_by_column_path(self, path, callback) - } - - fn insert_data_at_path(&self, path: &str, new_value: Value) -> Option { - insert_data_at_path(self, path, new_value) - } - - fn insert_data_at_member( - &mut self, - member: &PathMember, - new_value: Value, - ) -> Result<(), ShellError> { - insert_data_at_member(self, member, new_value) - } - - fn insert_data_at_column_path( - &self, - split_path: &ColumnPath, - new_value: Value, - ) -> Result { - insert_data_at_column_path(self, split_path, new_value) - } - - fn replace_data_at_column_path( - &self, - split_path: &ColumnPath, - replaced_value: Value, - ) -> Option { - replace_data_at_column_path(self, split_path, replaced_value) - } - - fn as_column_path(&self) -> Result, ShellError> { - as_column_path(self) - } - - fn as_path_member(&self) -> Result { - as_path_member(self) - } - - fn as_string(&self) -> Result { - as_string(self) - } -} - -pub(crate) fn get_data_by_member(value: &Value, name: &PathMember) -> Result { - match &value.value { - // If the value is a row, the member is a column name - UntaggedValue::Row(o) => match &name.unspanned { - // If the member is a string, get the data - UnspannedPathMember::String(string) => o - .get_data_by_key(string[..].spanned(name.span)) - .ok_or_else(|| { - ShellError::missing_property( - "row".spanned(value.tag.span), - string.spanned(name.span), - ) - }), - - // If the member is a number, it's an error - UnspannedPathMember::Int(_) => Err(ShellError::invalid_integer_index( - "row".spanned(value.tag.span), - name.span, - )), - }, - - // If the value is a table - UntaggedValue::Table(l) => { - match &name.unspanned { - // If the member is a string, map over the member - UnspannedPathMember::String(string) => { - let mut out = vec![]; - - for item in l { - if let Value { - value: UntaggedValue::Row(o), - .. - } = item - { - if let Some(v) = o.get_data_by_key(string[..].spanned(name.span)) { - out.push(v) - } - } - } - - if out.is_empty() { - Err(ShellError::missing_property( - "table".spanned(value.tag.span), - string.spanned(name.span), - )) - } else { - Ok(UntaggedValue::Table(out) - .into_value(Tag::new(value.anchor(), name.span))) - } - } - UnspannedPathMember::Int(int) => { - let index = int.to_usize().ok_or_else(|| { - ShellError::range_error( - ExpectedRange::Usize, - &"massive integer".spanned(name.span), - "indexing", - ) - })?; - - match get_data_by_index(value, index.spanned(value.tag.span)) { - Some(v) => Ok(v.clone()), - None => Err(ShellError::range_error( - 0..(l.len()), - &int.spanned(name.span), - "indexing", - )), - } - } - } - } - other => Err(ShellError::type_error( - "row or table", - other.type_name().spanned(value.tag.span), - )), - } -} - -pub fn get_data_by_column_path( - value: &Value, - path: &ColumnPath, - callback: Box ShellError>, -) -> Result { - let mut current = value.clone(); - - for p in path.iter() { - let value = get_data_by_member(¤t, p); - - match value { - Ok(v) => current = v.clone(), - Err(e) => return Err(callback((¤t.clone(), &p.clone(), e))), - } - } - - Ok(current) -} - -pub fn insert_data_at_path(value: &Value, path: &str, new_value: Value) -> Option { - let mut new_obj = value.clone(); - - let split_path: Vec<_> = path.split('.').collect(); - - if let UntaggedValue::Row(ref mut o) = new_obj.value { - let mut current = o; - - if split_path.len() == 1 { - // Special case for inserting at the top level - current.entries.insert( - path.to_string(), - new_value.value.clone().into_value(&value.tag), - ); - return Some(new_obj); - } - - for idx in 0..split_path.len() { - match current.entries.get_mut(split_path[idx]) { - Some(next) => { - if idx == (split_path.len() - 2) { - if let UntaggedValue::Row(o) = &mut next.value { - o.entries.insert( - split_path[idx + 1].to_string(), - new_value.value.clone().into_value(&value.tag), - ); - } - return Some(new_obj.clone()); - } else { - match next.value { - UntaggedValue::Row(ref mut o) => { - current = o; - } - _ => return None, - } - } - } - _ => return None, - } - } - } - - None -} - -pub fn insert_data_at_member( - value: &mut Value, - member: &PathMember, - new_value: Value, -) -> Result<(), ShellError> { - match &mut value.value { - UntaggedValue::Row(dict) => match &member.unspanned { - UnspannedPathMember::String(key) => { - dict.insert_data_at_key(key, new_value); - Ok(()) - } - UnspannedPathMember::Int(_) => Err(ShellError::type_error( - "column name", - "integer".spanned(member.span), - )), - }, - UntaggedValue::Table(array) => match &member.unspanned { - UnspannedPathMember::String(_) => Err(ShellError::type_error( - "list index", - "string".spanned(member.span), - )), - UnspannedPathMember::Int(int) => { - let int = int.to_usize().ok_or_else(|| { - ShellError::range_error( - ExpectedRange::Usize, - &"bigger number".spanned(member.span), - "inserting into a list", - ) - })?; - - insert_data_at_index(array, int.tagged(member.span), new_value.clone())?; - Ok(()) - } - }, - other => match &member.unspanned { - UnspannedPathMember::String(_) => Err(ShellError::type_error( - "row", - other.type_name().spanned(value.span()), - )), - UnspannedPathMember::Int(_) => Err(ShellError::type_error( - "table", - other.type_name().spanned(value.span()), - )), - }, - } -} - -pub fn insert_data_at_column_path( - value: &Value, - split_path: &ColumnPath, - new_value: Value, -) -> Result { - let (last, front) = split_path.split_last(); - let mut original = value.clone(); - - let mut current: &mut Value = &mut original; - - for member in front { - let type_name = current.spanned_type_name(); - - current = get_mut_data_by_member(current, &member).ok_or_else(|| { - ShellError::missing_property( - member.plain_string(std::usize::MAX).spanned(member.span), - type_name, - ) - })? - } - - insert_data_at_member(current, &last, new_value)?; - - Ok(original) -} - -pub fn replace_data_at_column_path( - value: &Value, - split_path: &ColumnPath, - replaced_value: Value, -) -> Option { - let mut new_obj: Value = value.clone(); - let mut current = &mut new_obj; - let split_path = split_path.members(); - - for idx in 0..split_path.len() { - match get_mut_data_by_member(current, &split_path[idx]) { - Some(next) => { - if idx == (split_path.len() - 1) { - *next = replaced_value.value.into_value(&value.tag); - return Some(new_obj); - } else { - current = next; - } - } - None => { - return None; - } - } - } - - None -} - -pub fn as_column_path(value: &Value) -> Result, ShellError> { - match &value.value { - UntaggedValue::Table(table) => { - let mut out: Vec = vec![]; - - for item in table { - out.push(as_path_member(item)?); - } - - Ok(ColumnPath::new(out).tagged(&value.tag)) - } - - UntaggedValue::Primitive(Primitive::String(s)) => { - Ok(ColumnPath::new(vec![PathMember::string(s, &value.tag.span)]).tagged(&value.tag)) - } - - UntaggedValue::Primitive(Primitive::ColumnPath(path)) => { - Ok(path.clone().tagged(value.tag.clone())) - } - - other => Err(ShellError::type_error( - "column path", - other.type_name().spanned(value.span()), - )), - } -} - -pub fn as_path_member(value: &Value) -> Result { - match &value.value { - UntaggedValue::Primitive(primitive) => match primitive { - Primitive::Int(int) => Ok(PathMember::int(int.clone(), value.tag.span)), - Primitive::String(string) => Ok(PathMember::string(string, value.tag.span)), - other => Err(ShellError::type_error( - "path member", - other.type_name().spanned(value.span()), - )), - }, - other => Err(ShellError::type_error( - "path member", - other.type_name().spanned(value.span()), - )), - } -} - -pub fn as_string(value: &Value) -> Result { - match &value.value { - UntaggedValue::Primitive(Primitive::String(s)) => Ok(s.clone()), - UntaggedValue::Primitive(Primitive::Boolean(x)) => Ok(format!("{}", x)), - UntaggedValue::Primitive(Primitive::Decimal(x)) => Ok(format!("{}", x)), - UntaggedValue::Primitive(Primitive::Int(x)) => Ok(format!("{}", x)), - UntaggedValue::Primitive(Primitive::Bytes(x)) => Ok(format!("{}", x)), - UntaggedValue::Primitive(Primitive::Path(x)) => Ok(format!("{}", x.display())), - UntaggedValue::Primitive(Primitive::ColumnPath(path)) => { - Ok(path.iter().map(|member| member.display()).join(".")) - } - - // TODO: this should definitely be more general with better errors - other => Err(ShellError::labeled_error( - "Expected string", - other.type_name(), - &value.tag, - )), - } -} - -fn insert_data_at_index( - list: &mut Vec, - index: Tagged, - new_value: Value, -) -> Result<(), ShellError> { - if list.len() >= index.item { - Err(ShellError::range_error( - 0..(list.len()), - &format_args!("{}", index.item).spanned(index.tag.span), - "insert at index", - )) - } else { - list[index.item] = new_value; - Ok(()) - } -} - -pub fn get_data<'value>(value: &'value Value, desc: &String) -> MaybeOwned<'value, Value> { - match &value.value { - UntaggedValue::Primitive(_) => MaybeOwned::Borrowed(value), - UntaggedValue::Row(o) => o.get_data(desc), - UntaggedValue::Block(_) | UntaggedValue::Table(_) | UntaggedValue::Error(_) => { - MaybeOwned::Owned(UntaggedValue::nothing().into_untagged_value()) - } - } -} - -pub(crate) fn get_data_by_index(value: &Value, idx: Spanned) -> Option { - match &value.value { - UntaggedValue::Table(value_set) => { - let value = value_set.get(idx.item)?; - Some( - value - .value - .clone() - .into_value(Tag::new(value.anchor(), idx.span)), - ) - } - _ => None, - } -} - -pub(crate) fn get_data_by_key(value: &Value, name: Spanned<&str>) -> Option { - match &value.value { - UntaggedValue::Row(o) => o.get_data_by_key(name), - UntaggedValue::Table(l) => { - let mut out = vec![]; - for item in l { - match item { - Value { - value: UntaggedValue::Row(o), - .. - } => match o.get_data_by_key(name) { - Some(v) => out.push(v), - None => out.push(UntaggedValue::nothing().into_untagged_value()), - }, - _ => out.push(UntaggedValue::nothing().into_untagged_value()), - } - } - - if !out.is_empty() { - Some(UntaggedValue::Table(out).into_value(name.span)) - } else { - None - } - } - _ => None, - } -} - -pub(crate) fn get_mut_data_by_member<'value>( - value: &'value mut Value, - name: &PathMember, -) -> Option<&'value mut Value> { - match &mut value.value { - UntaggedValue::Row(o) => match &name.unspanned { - UnspannedPathMember::String(string) => o.get_mut_data_by_key(&string), - UnspannedPathMember::Int(_) => None, - }, - UntaggedValue::Table(l) => match &name.unspanned { - UnspannedPathMember::String(string) => { - for item in l { - if let Value { - value: UntaggedValue::Row(o), - .. - } = item - { - if let Some(v) = o.get_mut_data_by_key(&string) { - return Some(v); - } - } - } - None - } - UnspannedPathMember::Int(int) => { - let index = int.to_usize()?; - l.get_mut(index) - } - }, - _ => None, - } -} diff --git a/src/deserializer.rs b/src/deserializer.rs index 76a32fe7cf..0b23b81657 100644 --- a/src/deserializer.rs +++ b/src/deserializer.rs @@ -1,8 +1,8 @@ -use crate::data::base::property_get::ValueExt; use log::trace; use nu_errors::{CoerceInto, ShellError}; use nu_protocol::{CallInfo, ColumnPath, Evaluate, Primitive, ShellTypeName, UntaggedValue, Value}; use nu_source::{HasSpan, SpannedItem, Tagged, TaggedItem}; +use nu_value_ext::ValueExt; use serde::de; use std::path::PathBuf; diff --git a/src/lib.rs b/src/lib.rs index ab33eb19da..b77a284b4e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -21,7 +21,6 @@ mod stream; mod utils; pub use crate::cli::cli; -pub use crate::data::base::property_get::ValueExt; pub use crate::data::config::{config_path, APP_INFO}; pub use crate::data::dict::TaggedListBuilder; pub use crate::data::primitive; @@ -29,6 +28,7 @@ pub use crate::data::value; pub use crate::env::host::BasicHost; pub use crate::utils::{AbsoluteFile, AbsolutePath, RelativePath}; pub use nu_parser::TokenTreeBuilder; +pub use nu_value_ext::ValueExt; pub use num_traits::cast::ToPrimitive; // TODO: Temporary redirect diff --git a/src/prelude.rs b/src/prelude.rs index 49a75fba43..307907e980 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -75,7 +75,6 @@ pub(crate) use crate::commands::command::{ }; pub(crate) use crate::context::CommandRegistry; pub(crate) use crate::context::Context; -pub(crate) use crate::data::base::property_get::ValueExt; pub(crate) use crate::data::types::ExtractType; pub(crate) use crate::data::value; pub(crate) use crate::env::host::handle_unexpected; @@ -94,6 +93,7 @@ pub(crate) use nu_source::{ b, AnchorLocation, DebugDocBuilder, HasSpan, PrettyDebug, PrettyDebugWithSource, Span, SpannedItem, Tag, TaggedItem, Text, }; +pub(crate) use nu_value_ext::ValueExt; pub(crate) use num_bigint::BigInt; pub(crate) use num_traits::cast::ToPrimitive; pub(crate) use serde::Deserialize;