diff --git a/Cargo.lock b/Cargo.lock index be761a0715..9222d51edd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1974,12 +1974,19 @@ dependencies = [ "nu-parser", "nu-protocol", "nu-source", + "nu-value-ext", + "nu_plugin_average", "nu_plugin_binaryview", "nu_plugin_fetch", + "nu_plugin_inc", + "nu_plugin_match", "nu_plugin_post", "nu_plugin_ps", + "nu_plugin_str", + "nu_plugin_sum", "nu_plugin_sys", "nu_plugin_textview", + "nu_plugin_tree", "num-bigint", "num-traits 0.2.10", "pin-utils", @@ -2086,11 +2093,14 @@ version = "0.1.0" dependencies = [ "ansi_term 0.12.1", "bigdecimal", + "byte-unit", "chrono", + "chrono-humanize", "derive-new", "getset", "indexmap", "language-reporting", + "natural", "nom 5.0.1", "nom-tracable", "nom_locate", @@ -2124,6 +2134,29 @@ dependencies = [ "termcolor", ] +[[package]] +name = "nu-value-ext" +version = "0.1.0" +dependencies = [ + "itertools 0.8.2", + "nu-build", + "nu-errors", + "nu-parser", + "nu-protocol", + "nu-source", + "num-traits 0.2.10", +] + +[[package]] +name = "nu_plugin_average" +version = "0.1.0" +dependencies = [ + "nu-build", + "nu-errors", + "nu-protocol", + "nu-source", +] + [[package]] name = "nu_plugin_binaryview" version = "0.1.0" @@ -2153,6 +2186,31 @@ dependencies = [ "url", ] +[[package]] +name = "nu_plugin_inc" +version = "0.1.0" +dependencies = [ + "indexmap", + "nu-build", + "nu-errors", + "nu-protocol", + "nu-source", + "nu-value-ext", + "semver", +] + +[[package]] +name = "nu_plugin_match" +version = "0.1.0" +dependencies = [ + "futures-preview", + "nu-build", + "nu-errors", + "nu-protocol", + "nu-source", + "regex", +] + [[package]] name = "nu_plugin_post" version = "0.1.0" @@ -2183,6 +2241,31 @@ dependencies = [ "pin-utils", ] +[[package]] +name = "nu_plugin_str" +version = "0.1.0" +dependencies = [ + "futures-preview", + "indexmap", + "nu-build", + "nu-errors", + "nu-protocol", + "nu-source", + "nu-value-ext", + "num-bigint", + "regex", +] + +[[package]] +name = "nu_plugin_sum" +version = "0.1.0" +dependencies = [ + "nu-build", + "nu-errors", + "nu-protocol", + "nu-source", +] + [[package]] name = "nu_plugin_sys" version = "0.1.0" @@ -2212,6 +2295,18 @@ dependencies = [ "url", ] +[[package]] +name = "nu_plugin_tree" +version = "0.1.0" +dependencies = [ + "derive-new", + "nu-build", + "nu-errors", + "nu-protocol", + "nu-source", + "ptree", +] + [[package]] name = "num-bigint" version = "0.2.3" diff --git a/Cargo.toml b/Cargo.toml index 6929c1e190..1de5d9b938 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,14 +16,21 @@ documentation = "https://book.nushell.sh" members = [ "crates/nu-errors", "crates/nu-source", + "crates/nu_plugin_average", "crates/nu_plugin_textview", "crates/nu_plugin_binaryview", "crates/nu_plugin_fetch", + "crates/nu_plugin_inc", + "crates/nu_plugin_match", "crates/nu_plugin_post", "crates/nu_plugin_ps", + "crates/nu_plugin_str", + "crates/nu_plugin_sum", "crates/nu_plugin_sys", + "crates/nu_plugin_tree", "crates/nu-protocol", "crates/nu-parser", + "crates/nu-value-ext", "crates/nu-build" ] @@ -34,12 +41,19 @@ nu-source = { version = "0.1.0", path = "./crates/nu-source" } nu-protocol = { version = "0.1.0", path = "./crates/nu-protocol" } nu-errors = { version = "0.1.0", path = "./crates/nu-errors" } nu-parser = { version = "0.1.0", path = "./crates/nu-parser" } +nu-value-ext = { version = "0.1.0", path = "./crates/nu-value-ext" } +nu_plugin_average = {version = "0.1.0", path = "./crates/nu_plugin_average", optional=true} nu_plugin_textview = {version = "0.1.0", path = "./crates/nu_plugin_textview", optional=true} nu_plugin_binaryview = {version = "0.1.0", path = "./crates/nu_plugin_binaryview", optional=true} nu_plugin_fetch = {version = "0.1.0", path = "./crates/nu_plugin_fetch", optional=true} +nu_plugin_inc = {version = "0.1.0", path = "./crates/nu_plugin_inc", optional=true} +nu_plugin_match = {version = "0.1.0", path = "./crates/nu_plugin_match", optional=true} nu_plugin_post = {version = "0.1.0", path = "./crates/nu_plugin_post", optional=true} nu_plugin_ps = {version = "0.1.0", path = "./crates/nu_plugin_ps", optional=true} +nu_plugin_str = {version = "0.1.0", path = "./crates/nu_plugin_str", optional=true} +nu_plugin_sum = {version = "0.1.0", path = "./crates/nu_plugin_sum", optional=true} nu_plugin_sys = {version = "0.1.0", path = "./crates/nu_plugin_sys", optional=true} +nu_plugin_tree = {version = "0.1.0", path = "./crates/nu_plugin_tree", optional=true} query_interface = "0.3.5" typetag = "0.1.4" @@ -95,7 +109,6 @@ shellexpand = "1.0.0" pin-utils = "0.1.0-alpha.4" num-bigint = { version = "0.2.3", features = ["serde"] } bigdecimal = { version = "0.1.0", features = ["serde"] } -natural = "0.3.0" serde_urlencoded = "0.6.1" trash = "1.0.0" regex = "1" @@ -105,18 +118,25 @@ calamine = "0.16" umask = "0.1" futures-util = "0.3.1" termcolor = "1.0.5" +natural = "0.3.0" clipboard = {version = "0.5", optional = true } ptree = {version = "0.2" } starship = { version = "0.28", optional = true} [features] -default = ["sys", "ps"] +default = ["sys", "ps", "match", "inc", "average", "str", "sum"] sys = ["nu_plugin_ps"] starship-prompt = ["starship"] textview = ["nu_plugin_textview"] binaryview = ["nu_plugin_binaryview"] ps = ["nu_plugin_ps"] +match = ["nu_plugin_match"] +tree = ["nu_plugin_tree"] +inc = ["nu_plugin_inc"] +average = ["nu_plugin_average"] +str = ["nu_plugin_str"] +sum = ["nu_plugin_sum"] #trace = ["nu-parser/trace"] [dependencies.rusqlite] @@ -135,35 +155,6 @@ nu-build = { version = "0.1.0", path = "./crates/nu-build" } name = "nu" path = "src/lib.rs" -[[bin]] -name = "nu_plugin_inc" -path = "src/plugins/inc.rs" - -[[bin]] -name = "nu_plugin_sum" -path = "src/plugins/sum.rs" - -[[bin]] -name = "nu_plugin_average" -path = "src/plugins/average.rs" - -[[bin]] -name = "nu_plugin_str" -path = "src/plugins/str.rs" - -[[bin]] -name = "nu_plugin_skip" -path = "src/plugins/skip.rs" - -[[bin]] -name = "nu_plugin_match" -path = "src/plugins/match.rs" - -[[bin]] -name = "nu_plugin_tree" -path = "src/plugins/tree.rs" -required-features = ["tree"] - [[bin]] name = "nu" path = "src/main.rs" diff --git a/crates/nu-protocol/Cargo.toml b/crates/nu-protocol/Cargo.toml index 7528ecd9d0..18dc016197 100644 --- a/crates/nu-protocol/Cargo.toml +++ b/crates/nu-protocol/Cargo.toml @@ -26,6 +26,9 @@ nom_locate = "1.0.0" nom-tracable = "0.4.1" typetag = "0.1.4" query_interface = "0.3.5" +byte-unit = "3.0.3" +chrono-humanize = "0.0.11" +natural = "0.3.0" # implement conversions subprocess = "0.1.18" diff --git a/crates/nu-protocol/src/lib.rs b/crates/nu-protocol/src/lib.rs index 69412b4b19..f97fed9139 100644 --- a/crates/nu-protocol/src/lib.rs +++ b/crates/nu-protocol/src/lib.rs @@ -17,8 +17,9 @@ pub use crate::return_value::{CommandAction, ReturnSuccess, ReturnValue}; pub use crate::signature::{NamedType, PositionalType, Signature}; pub use crate::syntax_shape::SyntaxShape; pub use crate::type_name::{PrettyType, ShellTypeName, SpannedTypeName}; -pub use crate::value::column_path::{ColumnPath, PathMember, UnspannedPathMember}; +pub use crate::value::column_path::{did_you_mean, ColumnPath, PathMember, UnspannedPathMember}; pub use crate::value::dict::{Dictionary, TaggedDictBuilder}; pub use crate::value::evaluate::{Evaluate, EvaluateTrait, Scope}; +pub use crate::value::primitive::format_primitive; pub use crate::value::primitive::Primitive; pub use crate::value::{UntaggedValue, Value}; diff --git a/crates/nu-protocol/src/value/column_path.rs b/crates/nu-protocol/src/value/column_path.rs index 5695b59572..b977d099f4 100644 --- a/crates/nu-protocol/src/value/column_path.rs +++ b/crates/nu-protocol/src/value/column_path.rs @@ -1,3 +1,4 @@ +use crate::Value; use derive_new::new; use getset::Getters; use nu_source::{b, span_for_spanned_list, DebugDocBuilder, HasFallibleSpan, PrettyDebug, Span}; @@ -85,3 +86,29 @@ impl PathMember { UnspannedPathMember::Int(int.into()).into_path_member(span) } } + +pub fn did_you_mean(obj_source: &Value, field_tried: &PathMember) -> Option> { + let field_tried = match &field_tried.unspanned { + UnspannedPathMember::String(string) => string.clone(), + UnspannedPathMember::Int(int) => format!("{}", int), + }; + + let possibilities = obj_source.data_descriptors(); + + let mut possible_matches: Vec<_> = possibilities + .into_iter() + .map(|x| { + let word = x.clone(); + let distance = natural::distance::levenshtein_distance(&word, &field_tried); + + (distance, word) + }) + .collect(); + + if !possible_matches.is_empty() { + possible_matches.sort(); + Some(possible_matches) + } else { + None + } +} diff --git a/crates/nu-protocol/src/value/primitive.rs b/crates/nu-protocol/src/value/primitive.rs index 80092d3c48..92bb3ee05d 100644 --- a/crates/nu-protocol/src/value/primitive.rs +++ b/crates/nu-protocol/src/value/primitive.rs @@ -3,6 +3,8 @@ use crate::value::column_path::ColumnPath; use crate::value::{serde_bigdecimal, serde_bigint}; use bigdecimal::BigDecimal; use chrono::{DateTime, Utc}; +use chrono_humanize::Humanize; +use nu_source::PrettyDebug; use num_bigint::BigInt; use num_traits::cast::FromPrimitive; use serde::{Deserialize, Serialize}; @@ -65,3 +67,74 @@ impl ShellTypeName for Primitive { } } } + +pub fn format_primitive(primitive: &Primitive, field_name: Option<&String>) -> String { + match primitive { + Primitive::Nothing => String::new(), + Primitive::BeginningOfStream => String::new(), + Primitive::EndOfStream => String::new(), + Primitive::Path(p) => format!("{}", p.display()), + Primitive::Bytes(b) => { + let byte = byte_unit::Byte::from_bytes(*b as u128); + + if byte.get_bytes() == 0u128 { + return "—".to_string(); + } + + let byte = byte.get_appropriate_unit(false); + + match byte.get_unit() { + byte_unit::ByteUnit::B => format!("{} B ", byte.get_value()), + _ => byte.format(1).to_string(), + } + } + Primitive::Duration(sec) => format_duration(*sec), + Primitive::Int(i) => i.to_string(), + Primitive::Decimal(decimal) => decimal.to_string(), + Primitive::Pattern(s) => s.to_string(), + Primitive::String(s) => s.to_owned(), + Primitive::Line(s) => s.to_owned(), + Primitive::ColumnPath(p) => { + let mut members = p.iter(); + let mut f = String::new(); + + f.push_str( + &members + .next() + .expect("BUG: column path with zero members") + .display(), + ); + + for member in members { + f.push_str("."); + f.push_str(&member.display()) + } + + f + } + Primitive::Boolean(b) => match (b, field_name) { + (true, None) => "Yes", + (false, None) => "No", + (true, Some(s)) if !s.is_empty() => s, + (false, Some(s)) if !s.is_empty() => "", + (true, Some(_)) => "Yes", + (false, Some(_)) => "No", + } + .to_owned(), + Primitive::Binary(_) => "".to_owned(), + Primitive::Date(d) => d.humanize().to_string(), + } +} +fn format_duration(sec: u64) -> String { + let (minutes, seconds) = (sec / 60, sec % 60); + let (hours, minutes) = (minutes / 60, minutes % 60); + let (days, hours) = (hours / 24, hours % 24); + + match (days, hours, minutes, seconds) { + (0, 0, 0, 1) => "1 sec".to_owned(), + (0, 0, 0, s) => format!("{} secs", s), + (0, 0, m, s) => format!("{}:{:02}", m, s), + (0, h, m, s) => format!("{}:{:02}:{:02}", h, m, s), + (d, h, m, s) => format!("{}:{:02}:{:02}:{:02}", d, h, m, s), + } +} diff --git a/crates/nu-value-ext/Cargo.toml b/crates/nu-value-ext/Cargo.toml new file mode 100644 index 0000000000..a5d79f898a --- /dev/null +++ b/crates/nu-value-ext/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "nu-value-ext" +version = "0.1.0" +authors = ["Yehuda Katz ", "Jonathan Turner ", "Andrés N. Robalino "] +edition = "2018" +description = "A source string characterizer for Nushell" +license = "MIT" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +nu-source = { path = "../nu-source" } +nu-errors = { path = "../nu-errors" } +nu-parser = { path = "../nu-parser" } +nu-protocol = { path = "../nu-protocol" } + +num-traits = "0.2.10" +itertools = "0.8.2" + +[build-dependencies] +nu-build = { version = "0.1.0", path = "../nu-build" } diff --git a/crates/nu-value-ext/build.rs b/crates/nu-value-ext/build.rs new file mode 100644 index 0000000000..b7511cfc6a --- /dev/null +++ b/crates/nu-value-ext/build.rs @@ -0,0 +1,3 @@ +fn main() -> Result<(), Box> { + nu_build::build() +} diff --git a/crates/nu-value-ext/src/lib.rs b/crates/nu-value-ext/src/lib.rs new file mode 100644 index 0000000000..990cc3b124 --- /dev/null +++ b/crates/nu-value-ext/src/lib.rs @@ -0,0 +1,515 @@ +use itertools::Itertools; +use nu_errors::{ExpectedRange, ShellError}; +use nu_protocol::{ + ColumnPath, MaybeOwned, PathMember, Primitive, ShellTypeName, SpannedTypeName, + UnspannedPathMember, UntaggedValue, Value, +}; +use nu_source::{HasSpan, PrettyDebug, Spanned, SpannedItem, Tag, Tagged, TaggedItem}; +use num_traits::cast::ToPrimitive; + +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/crates/nu_plugin_average/Cargo.toml b/crates/nu_plugin_average/Cargo.toml new file mode 100644 index 0000000000..fb03644ca6 --- /dev/null +++ b/crates/nu_plugin_average/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "nu_plugin_average" +version = "0.1.0" +authors = ["Yehuda Katz ", "Jonathan Turner ", "Andrés N. Robalino "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +nu-protocol = { path = "../nu-protocol" } +nu-source = { path = "../nu-source" } +nu-errors = { path = "../nu-errors" } + +[build-dependencies] +nu-build = { version = "0.1.0", path = "../nu-build" } diff --git a/crates/nu_plugin_average/build.rs b/crates/nu_plugin_average/build.rs new file mode 100644 index 0000000000..b7511cfc6a --- /dev/null +++ b/crates/nu_plugin_average/build.rs @@ -0,0 +1,3 @@ +fn main() -> Result<(), Box> { + nu_build::build() +} diff --git a/src/plugins/average.rs b/crates/nu_plugin_average/src/main.rs similarity index 97% rename from src/plugins/average.rs rename to crates/nu_plugin_average/src/main.rs index 3ed18d971a..80685236ee 100644 --- a/src/plugins/average.rs +++ b/crates/nu_plugin_average/src/main.rs @@ -1,7 +1,7 @@ -use nu::{serve_plugin, Plugin}; use nu_errors::{CoerceInto, ShellError}; use nu_protocol::{ - CallInfo, Primitive, ReturnSuccess, ReturnValue, Signature, UntaggedValue, Value, + serve_plugin, CallInfo, Plugin, Primitive, ReturnSuccess, ReturnValue, Signature, + UntaggedValue, Value, }; use nu_source::TaggedItem; diff --git a/crates/nu_plugin_inc/Cargo.toml b/crates/nu_plugin_inc/Cargo.toml new file mode 100644 index 0000000000..09fdf6fa98 --- /dev/null +++ b/crates/nu_plugin_inc/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "nu_plugin_inc" +version = "0.1.0" +authors = ["Yehuda Katz ", "Jonathan Turner ", "Andrés N. Robalino "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +nu-protocol = { path = "../nu-protocol" } +nu-source = { path = "../nu-source" } +nu-errors = { path = "../nu-errors" } +nu-value-ext = { path = "../nu-value-ext" } +indexmap = "1.3.0" +semver = "0.9.0" + +[build-dependencies] +nu-build = { version = "0.1.0", path = "../nu-build" } diff --git a/crates/nu_plugin_inc/build.rs b/crates/nu_plugin_inc/build.rs new file mode 100644 index 0000000000..b7511cfc6a --- /dev/null +++ b/crates/nu_plugin_inc/build.rs @@ -0,0 +1,3 @@ +fn main() -> Result<(), Box> { + nu_build::build() +} diff --git a/src/plugins/inc.rs b/crates/nu_plugin_inc/src/main.rs similarity index 98% rename from src/plugins/inc.rs rename to crates/nu_plugin_inc/src/main.rs index 574413c3f4..3fd6ca89e2 100644 --- a/src/plugins/inc.rs +++ b/crates/nu_plugin_inc/src/main.rs @@ -1,10 +1,10 @@ -use nu::{did_you_mean, serve_plugin, Plugin, ValueExt}; use nu_errors::ShellError; use nu_protocol::{ - CallInfo, ColumnPath, Primitive, ReturnSuccess, ReturnValue, ShellTypeName, Signature, - SyntaxShape, UntaggedValue, Value, + did_you_mean, serve_plugin, CallInfo, ColumnPath, Plugin, Primitive, ReturnSuccess, + ReturnValue, ShellTypeName, Signature, SyntaxShape, UntaggedValue, Value, }; use nu_source::{span_for_spanned_list, HasSpan, SpannedItem, Tagged}; +use nu_value_ext::ValueExt; enum Action { SemVerAction(SemVerAction), @@ -218,11 +218,11 @@ mod tests { use super::{Inc, SemVerAction}; use indexmap::IndexMap; - use nu::{Plugin, TaggedDictBuilder}; use nu_protocol::{ CallInfo, EvaluatedArgs, PathMember, ReturnSuccess, UnspannedPathMember, UntaggedValue, Value, }; + use nu_protocol::{Plugin, TaggedDictBuilder}; use nu_source::{Span, Tag}; struct CallStub { diff --git a/crates/nu_plugin_match/Cargo.toml b/crates/nu_plugin_match/Cargo.toml new file mode 100644 index 0000000000..27a50d9701 --- /dev/null +++ b/crates/nu_plugin_match/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "nu_plugin_match" +version = "0.1.0" +authors = ["Yehuda Katz ", "Jonathan Turner ", "Andrés N. Robalino "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +nu-protocol = { path = "../nu-protocol" } +nu-source = { path = "../nu-source" } +nu-errors = { path = "../nu-errors" } +futures-preview = { version = "=0.3.0-alpha.19", features = ["compat", "io-compat"] } +regex = "1" + +[build-dependencies] +nu-build = { version = "0.1.0", path = "../nu-build" } diff --git a/crates/nu_plugin_match/build.rs b/crates/nu_plugin_match/build.rs new file mode 100644 index 0000000000..b7511cfc6a --- /dev/null +++ b/crates/nu_plugin_match/build.rs @@ -0,0 +1,3 @@ +fn main() -> Result<(), Box> { + nu_build::build() +} diff --git a/src/plugins/match.rs b/crates/nu_plugin_match/src/main.rs similarity index 95% rename from src/plugins/match.rs rename to crates/nu_plugin_match/src/main.rs index 830ec4af27..2a9783aead 100644 --- a/src/plugins/match.rs +++ b/crates/nu_plugin_match/src/main.rs @@ -1,7 +1,7 @@ -use nu::{serve_plugin, Plugin}; use nu_errors::ShellError; use nu_protocol::{ - CallInfo, Primitive, ReturnSuccess, ReturnValue, Signature, SyntaxShape, UntaggedValue, Value, + serve_plugin, CallInfo, Plugin, Primitive, ReturnSuccess, ReturnValue, Signature, SyntaxShape, + UntaggedValue, Value, }; use regex::Regex; diff --git a/crates/nu_plugin_str/Cargo.toml b/crates/nu_plugin_str/Cargo.toml new file mode 100644 index 0000000000..999683aa04 --- /dev/null +++ b/crates/nu_plugin_str/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "nu_plugin_str" +version = "0.1.0" +authors = ["Yehuda Katz ", "Jonathan Turner ", "Andrés N. Robalino "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +nu-protocol = { path = "../nu-protocol" } +nu-source = { path = "../nu-source" } +nu-errors = { path = "../nu-errors" } +nu-value-ext = { path = "../nu-value-ext" } + +futures-preview = { version = "=0.3.0-alpha.19", features = ["compat", "io-compat"] } +regex = "1" +indexmap = "1.3.0" +num-bigint = "0.2.3" + +[build-dependencies] +nu-build = { version = "0.1.0", path = "../nu-build" } diff --git a/crates/nu_plugin_str/build.rs b/crates/nu_plugin_str/build.rs new file mode 100644 index 0000000000..b7511cfc6a --- /dev/null +++ b/crates/nu_plugin_str/build.rs @@ -0,0 +1,3 @@ +fn main() -> Result<(), Box> { + nu_build::build() +} diff --git a/src/plugins/str.rs b/crates/nu_plugin_str/src/main.rs similarity index 98% rename from src/plugins/str.rs rename to crates/nu_plugin_str/src/main.rs index 4b819dc4b4..b7aaf8ea08 100644 --- a/src/plugins/str.rs +++ b/crates/nu_plugin_str/src/main.rs @@ -1,10 +1,10 @@ -use nu::{did_you_mean, serve_plugin, Plugin, ValueExt}; use nu_errors::ShellError; use nu_protocol::{ - CallInfo, ColumnPath, Primitive, ReturnSuccess, ReturnValue, ShellTypeName, Signature, - SyntaxShape, UntaggedValue, Value, + did_you_mean, serve_plugin, CallInfo, ColumnPath, Plugin, Primitive, ReturnSuccess, + ReturnValue, ShellTypeName, Signature, SyntaxShape, UntaggedValue, Value, }; use nu_source::{span_for_spanned_list, Tagged}; +use nu_value_ext::ValueExt; use regex::Regex; use std::cmp; @@ -313,9 +313,12 @@ fn main() { mod tests { use super::{Action, ReplaceAction, Str}; use indexmap::IndexMap; - use nu::{Plugin, TaggedDictBuilder, ValueExt}; - use nu_protocol::{CallInfo, EvaluatedArgs, Primitive, ReturnSuccess, UntaggedValue, Value}; + use nu_protocol::{ + CallInfo, EvaluatedArgs, Plugin, Primitive, ReturnSuccess, TaggedDictBuilder, + UntaggedValue, Value, + }; use nu_source::Tag; + use nu_value_ext::ValueExt; use num_bigint::BigInt; fn string(input: impl Into) -> Value { diff --git a/crates/nu_plugin_sum/Cargo.toml b/crates/nu_plugin_sum/Cargo.toml new file mode 100644 index 0000000000..77bbcd7411 --- /dev/null +++ b/crates/nu_plugin_sum/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "nu_plugin_sum" +version = "0.1.0" +authors = ["Yehuda Katz ", "Jonathan Turner ", "Andrés N. Robalino "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +nu-protocol = { path = "../nu-protocol" } +nu-source = { path = "../nu-source" } +nu-errors = { path = "../nu-errors" } + +[build-dependencies] +nu-build = { version = "0.1.0", path = "../nu-build" } diff --git a/crates/nu_plugin_sum/build.rs b/crates/nu_plugin_sum/build.rs new file mode 100644 index 0000000000..b7511cfc6a --- /dev/null +++ b/crates/nu_plugin_sum/build.rs @@ -0,0 +1,3 @@ +fn main() -> Result<(), Box> { + nu_build::build() +} diff --git a/src/plugins/sum.rs b/crates/nu_plugin_sum/src/main.rs similarity index 96% rename from src/plugins/sum.rs rename to crates/nu_plugin_sum/src/main.rs index c46f42b465..58c697cef0 100644 --- a/src/plugins/sum.rs +++ b/crates/nu_plugin_sum/src/main.rs @@ -1,7 +1,7 @@ -use nu::{serve_plugin, Plugin}; use nu_errors::ShellError; use nu_protocol::{ - CallInfo, Primitive, ReturnSuccess, ReturnValue, Signature, UntaggedValue, Value, + serve_plugin, CallInfo, Plugin, Primitive, ReturnSuccess, ReturnValue, Signature, + UntaggedValue, Value, }; struct Sum { diff --git a/crates/nu_plugin_tree/Cargo.toml b/crates/nu_plugin_tree/Cargo.toml new file mode 100644 index 0000000000..020c2e58e4 --- /dev/null +++ b/crates/nu_plugin_tree/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "nu_plugin_tree" +version = "0.1.0" +authors = ["Yehuda Katz ", "Jonathan Turner ", "Andrés N. Robalino "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +nu-protocol = { path = "../nu-protocol" } +nu-source = { path = "../nu-source" } +nu-errors = { path = "../nu-errors" } +ptree = {version = "0.2" } +derive-new = "0.5.8" + +[build-dependencies] +nu-build = { version = "0.1.0", path = "../nu-build" } diff --git a/crates/nu_plugin_tree/build.rs b/crates/nu_plugin_tree/build.rs new file mode 100644 index 0000000000..b7511cfc6a --- /dev/null +++ b/crates/nu_plugin_tree/build.rs @@ -0,0 +1,3 @@ +fn main() -> Result<(), Box> { + nu_build::build() +} diff --git a/src/plugins/tree.rs b/crates/nu_plugin_tree/src/main.rs similarity index 81% rename from src/plugins/tree.rs rename to crates/nu_plugin_tree/src/main.rs index acddcd612f..e82a34510a 100644 --- a/src/plugins/tree.rs +++ b/crates/nu_plugin_tree/src/main.rs @@ -1,5 +1,8 @@ use derive_new::new; -use nu::{serve_plugin, CallInfo, Plugin, ShellError, Signature, Tagged, Value}; +use nu_errors::ShellError; +use nu_protocol::{ + format_primitive, serve_plugin, CallInfo, Plugin, Signature, UntaggedValue, Value, +}; use ptree::item::StringItem; use ptree::output::print_tree_with; use ptree::print_config::PrintConfig; @@ -12,10 +15,10 @@ pub struct TreeView { } impl TreeView { - fn from_value_helper(value: &Value, mut builder: &mut TreeBuilder) { + fn from_value_helper(value: &UntaggedValue, mut builder: &mut TreeBuilder) { match value { UntaggedValue::Primitive(p) => { - let _ = builder.add_empty_child(p.format(None)); + let _ = builder.add_empty_child(format_primitive(p, None)); } UntaggedValue::Row(o) => { for (k, v) in o.entries.iter() { @@ -29,8 +32,7 @@ impl TreeView { Self::from_value_helper(elem, builder); } } - UntaggedValue::Block(_) => {} - UntaggedValue::Binary(_) => {} + _ => {} } } @@ -41,9 +43,12 @@ impl TreeView { let mut builder = &mut tree; for desc in descs { - let value = value.get_data(&desc); + let value = match &value.value { + UntaggedValue::Row(d) => d.get_data(&desc).borrow().clone(), + _ => value.clone(), + }; builder = builder.begin_child(desc.clone()); - Self::from_value_helper(value.borrow(), &mut builder); + Self::from_value_helper(&value, &mut builder); builder = builder.end_child(); //entries.push((desc.name.clone(), value.borrow().copy())) } diff --git a/src/cli.rs b/src/cli.rs index cbbde8c1bc..b31a71cbe1 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -231,22 +231,75 @@ pub async fn cli() -> Result<(), Box> { use crate::commands::*; context.add_commands(vec![ + // System/file operations whole_stream_command(PWD), whole_stream_command(LS), whole_stream_command(CD), + whole_stream_command(Env), + per_item_command(Remove), + per_item_command(Open), + whole_stream_command(Config), + per_item_command(Help), + per_item_command(History), + whole_stream_command(Save), + per_item_command(Cpy), + whole_stream_command(Date), + per_item_command(Mkdir), + per_item_command(Move), + whole_stream_command(Version), + whole_stream_command(What), + whole_stream_command(Which), + whole_stream_command(Debug), + // Statistics whole_stream_command(Size), - whole_stream_command(Nth), + whole_stream_command(Count), + // Metadata + whole_stream_command(Tags), + // Shells whole_stream_command(Next), whole_stream_command(Previous), whole_stream_command(Shells), + per_item_command(Enter), + whole_stream_command(Exit), + // Viewers + whole_stream_command(Autoview), + whole_stream_command(Table), + // Text manipulation whole_stream_command(SplitColumn), whole_stream_command(SplitRow), whole_stream_command(Lines), + whole_stream_command(Trim), + per_item_command(Echo), + per_item_command(Parse), + // Column manipulation whole_stream_command(Reject), + whole_stream_command(Pick), + whole_stream_command(Get), + per_item_command(Edit), + per_item_command(Insert), + whole_stream_command(SplitBy), + // Row manipulation whole_stream_command(Reverse), whole_stream_command(Append), whole_stream_command(Prepend), - whole_stream_command(Trim), + whole_stream_command(SortBy), + whole_stream_command(GroupBy), + whole_stream_command(First), + whole_stream_command(Last), + whole_stream_command(Skip), + whole_stream_command(Nth), + per_item_command(Format), + per_item_command(Where), + whole_stream_command(Compact), + whole_stream_command(Default), + whole_stream_command(SkipWhile), + whole_stream_command(Range), + // Table manipulation + whole_stream_command(Wrap), + whole_stream_command(Pivot), + // Data processing + whole_stream_command(Histogram), + // File format output whole_stream_command(ToBSON), whole_stream_command(ToCSV), whole_stream_command(ToJSON), @@ -256,13 +309,7 @@ pub async fn cli() -> Result<(), Box> { whole_stream_command(ToTSV), whole_stream_command(ToURL), whole_stream_command(ToYAML), - whole_stream_command(SortBy), - whole_stream_command(GroupBy), - whole_stream_command(Tags), - whole_stream_command(Count), - whole_stream_command(First), - whole_stream_command(Last), - whole_stream_command(Env), + // File format input whole_stream_command(FromCSV), whole_stream_command(FromTSV), whole_stream_command(FromSSV), @@ -277,40 +324,6 @@ pub async fn cli() -> Result<(), Box> { whole_stream_command(FromXML), whole_stream_command(FromYAML), whole_stream_command(FromYML), - whole_stream_command(Pick), - whole_stream_command(Get), - whole_stream_command(Histogram), - per_item_command(Remove), - per_item_command(Open), - per_item_command(Where), - per_item_command(Echo), - per_item_command(Edit), - per_item_command(Insert), - per_item_command(Format), - per_item_command(Parse), - whole_stream_command(Config), - whole_stream_command(Compact), - whole_stream_command(Default), - whole_stream_command(SkipWhile), - per_item_command(Enter), - per_item_command(Help), - per_item_command(History), - whole_stream_command(Exit), - whole_stream_command(Autoview), - whole_stream_command(Pivot), - per_item_command(Cpy), - whole_stream_command(Date), - per_item_command(Mkdir), - per_item_command(Move), - whole_stream_command(Save), - whole_stream_command(SplitBy), - whole_stream_command(Table), - whole_stream_command(Version), - whole_stream_command(What), - whole_stream_command(Which), - whole_stream_command(Debug), - whole_stream_command(Range), - whole_stream_command(Wrap), ]); cfg_if::cfg_if! { diff --git a/src/commands.rs b/src/commands.rs index 4c19bb4675..c1d73133a6 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -71,6 +71,7 @@ pub(crate) mod rm; pub(crate) mod save; pub(crate) mod shells; pub(crate) mod size; +pub(crate) mod skip; pub(crate) mod skip_while; pub(crate) mod sort_by; pub(crate) mod split_by; @@ -164,6 +165,7 @@ pub(crate) use rm::Remove; pub(crate) use save::Save; pub(crate) use shells::Shells; pub(crate) use size::Size; +pub(crate) use skip::Skip; pub(crate) use skip_while::SkipWhile; pub(crate) use sort_by::SortBy; pub(crate) use split_by::SplitBy; diff --git a/src/commands/get.rs b/src/commands/get.rs index 0bc449beda..d1f242169f 100644 --- a/src/commands/get.rs +++ b/src/commands/get.rs @@ -2,12 +2,12 @@ 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 crate::utils::did_you_mean; use futures_util::pin_mut; use log::trace; use nu_errors::ShellError; use nu_protocol::{ - ColumnPath, ReturnSuccess, ReturnValue, Signature, SyntaxShape, UntaggedValue, Value, + did_you_mean, ColumnPath, ReturnSuccess, ReturnValue, Signature, SyntaxShape, UntaggedValue, + Value, }; use nu_source::{span_for_spanned_list, PrettyDebug}; diff --git a/src/commands/skip.rs b/src/commands/skip.rs new file mode 100644 index 0000000000..88b04058cf --- /dev/null +++ b/src/commands/skip.rs @@ -0,0 +1,47 @@ +use crate::commands::WholeStreamCommand; +use crate::context::CommandRegistry; +use crate::prelude::*; +use nu_errors::ShellError; +use nu_protocol::{Signature, SyntaxShape}; +use nu_source::Tagged; + +pub struct Skip; + +#[derive(Deserialize)] +pub struct SkipArgs { + rows: Option>, +} + +impl WholeStreamCommand for Skip { + fn name(&self) -> &str { + "skip" + } + + fn signature(&self) -> Signature { + Signature::build("skip").optional("rows", SyntaxShape::Int, "how many rows to skip") + } + + fn usage(&self) -> &str { + "Skip some number of rows." + } + + fn run( + &self, + args: CommandArgs, + registry: &CommandRegistry, + ) -> Result { + args.process(registry, skip)?.run() + } +} + +fn skip(SkipArgs { rows }: SkipArgs, context: RunnableContext) -> Result { + let rows_desired = if let Some(quantity) = rows { + *quantity + } else { + 1 + }; + + Ok(OutputStream::from_input( + context.input.values.skip(rows_desired), + )) +} diff --git a/src/data/base/shape.rs b/src/data/base/shape.rs index 512af2109f..44521c772a 100644 --- a/src/data/base/shape.rs +++ b/src/data/base/shape.rs @@ -1,4 +1,3 @@ -use crate::data::primitive::format_primitive; use crate::prelude::*; use chrono::{DateTime, Utc}; use chrono_humanize::Humanize; @@ -6,8 +5,8 @@ use derive_new::new; use indexmap::IndexMap; use nu_errors::ShellError; use nu_protocol::{ - ColumnPath, Dictionary, Evaluate, Primitive, ShellTypeName, TaggedDictBuilder, UntaggedValue, - Value, + format_primitive, ColumnPath, Dictionary, Evaluate, Primitive, ShellTypeName, + TaggedDictBuilder, UntaggedValue, Value, }; use nu_source::{b, DebugDoc, PrettyDebug}; use std::collections::BTreeMap; diff --git a/src/data/primitive.rs b/src/data/primitive.rs index cd4267905c..b34af13d01 100644 --- a/src/data/primitive.rs +++ b/src/data/primitive.rs @@ -1,7 +1,5 @@ -use chrono_humanize::Humanize; use nu_parser::Number; use nu_protocol::Primitive; -use nu_source::PrettyDebug; pub fn number(number: impl Into) -> Primitive { let number = number.into(); @@ -12,64 +10,6 @@ pub fn number(number: impl Into) -> Primitive { } } -pub fn format_primitive(primitive: &Primitive, field_name: Option<&String>) -> String { - match primitive { - Primitive::Nothing => String::new(), - Primitive::BeginningOfStream => String::new(), - Primitive::EndOfStream => String::new(), - Primitive::Path(p) => format!("{}", p.display()), - Primitive::Bytes(b) => { - let byte = byte_unit::Byte::from_bytes(*b as u128); - - if byte.get_bytes() == 0u128 { - return "—".to_string(); - } - - let byte = byte.get_appropriate_unit(false); - - match byte.get_unit() { - byte_unit::ByteUnit::B => format!("{} B ", byte.get_value()), - _ => byte.format(1).to_string(), - } - } - Primitive::Duration(sec) => format_duration(*sec), - Primitive::Int(i) => i.to_string(), - Primitive::Decimal(decimal) => decimal.to_string(), - Primitive::Pattern(s) => s.to_string(), - Primitive::String(s) => s.to_owned(), - Primitive::Line(s) => s.to_owned(), - Primitive::ColumnPath(p) => { - let mut members = p.iter(); - let mut f = String::new(); - - f.push_str( - &members - .next() - .expect("BUG: column path with zero members") - .display(), - ); - - for member in members { - f.push_str("."); - f.push_str(&member.display()) - } - - f - } - Primitive::Boolean(b) => match (b, field_name) { - (true, None) => "Yes", - (false, None) => "No", - (true, Some(s)) if !s.is_empty() => s, - (false, Some(s)) if !s.is_empty() => "", - (true, Some(_)) => "Yes", - (false, Some(_)) => "No", - } - .to_owned(), - Primitive::Binary(_) => "".to_owned(), - Primitive::Date(d) => d.humanize().to_string(), - } -} - pub fn style_primitive(primitive: &Primitive) -> &'static str { match primitive { Primitive::Bytes(0) => "c", // centre 'missing' indicator @@ -77,17 +17,3 @@ pub fn style_primitive(primitive: &Primitive) -> &'static str { _ => "", } } - -fn format_duration(sec: u64) -> String { - let (minutes, seconds) = (sec / 60, sec % 60); - let (hours, minutes) = (minutes / 60, minutes % 60); - let (days, hours) = (hours / 24, hours % 24); - - match (days, hours, minutes, seconds) { - (0, 0, 0, 1) => "1 sec".to_owned(), - (0, 0, 0, s) => format!("{} secs", s), - (0, 0, m, s) => format!("{}:{:02}", m, s), - (0, h, m, s) => format!("{}:{:02}:{:02}", h, m, s), - (d, h, m, s) => format!("{}:{:02}:{:02}:{:02}", d, h, m, s), - } -} diff --git a/src/format/generic.rs b/src/format/generic.rs index cc469f0d61..336b76e0e4 100644 --- a/src/format/generic.rs +++ b/src/format/generic.rs @@ -1,10 +1,9 @@ -use crate::data::primitive::format_primitive; use crate::data::value::format_leaf; use crate::format::{EntriesView, RenderView, TableView}; use crate::prelude::*; use derive_new::new; use nu_errors::ShellError; -use nu_protocol::{UntaggedValue, Value}; +use nu_protocol::{format_primitive, UntaggedValue, Value}; // A list is printed one line at a time with an optional separator between groups #[derive(new)] diff --git a/src/lib.rs b/src/lib.rs index 9d24f24106..ab33eb19da 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -27,9 +27,9 @@ pub use crate::data::dict::TaggedListBuilder; pub use crate::data::primitive; pub use crate::data::value; pub use crate::env::host::BasicHost; -pub use crate::utils::{did_you_mean, AbsoluteFile, AbsolutePath, RelativePath}; +pub use crate::utils::{AbsoluteFile, AbsolutePath, RelativePath}; pub use nu_parser::TokenTreeBuilder; pub use num_traits::cast::ToPrimitive; // TODO: Temporary redirect -pub use nu_protocol::{serve_plugin, Plugin, TaggedDictBuilder}; +pub use nu_protocol::{did_you_mean, serve_plugin, Plugin, TaggedDictBuilder}; diff --git a/src/plugins/skip.rs b/src/plugins/skip.rs deleted file mode 100644 index 1bf0982125..0000000000 --- a/src/plugins/skip.rs +++ /dev/null @@ -1,61 +0,0 @@ -use nu::{serve_plugin, Plugin}; -use nu_errors::{CoerceInto, ShellError}; -use nu_protocol::{ - CallInfo, Primitive, ReturnSuccess, ReturnValue, Signature, SyntaxShape, UntaggedValue, Value, -}; -use nu_source::TaggedItem; - -struct Skip { - skip_amount: i64, -} - -impl Skip { - fn new() -> Skip { - Skip { skip_amount: 0 } - } -} - -impl Plugin for Skip { - fn config(&mut self) -> Result { - Ok(Signature::build("skip") - .desc("Skip a number of rows") - .rest(SyntaxShape::Number, "the number of rows to skip") - .filter()) - } - fn begin_filter(&mut self, call_info: CallInfo) -> Result, ShellError> { - if let Some(args) = call_info.args.positional { - for arg in args { - match arg { - Value { - value: UntaggedValue::Primitive(Primitive::Int(i)), - tag, - } => { - self.skip_amount = i.tagged(tag).coerce_into("converting for skip")?; - } - _ => { - return Err(ShellError::labeled_error( - "Unrecognized type in params", - "expected an integer", - arg.tag(), - )) - } - } - } - } - - Ok(vec![]) - } - - fn filter(&mut self, input: Value) -> Result, ShellError> { - if self.skip_amount == 0 { - Ok(vec![ReturnSuccess::value(input)]) - } else { - self.skip_amount -= 1; - Ok(vec![]) - } - } -} - -fn main() { - serve_plugin(&mut Skip::new()); -} diff --git a/src/utils.rs b/src/utils.rs index dc9fed32e3..e7cad7f529 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,35 +1,9 @@ use nu_errors::ShellError; -use nu_protocol::{PathMember, UnspannedPathMember, UntaggedValue, Value}; +use nu_protocol::{UntaggedValue, Value}; use nu_source::{b, DebugDocBuilder, PrettyDebug}; use std::ops::Div; use std::path::{Component, Path, PathBuf}; -pub fn did_you_mean(obj_source: &Value, field_tried: &PathMember) -> Option> { - let field_tried = match &field_tried.unspanned { - UnspannedPathMember::String(string) => string.clone(), - UnspannedPathMember::Int(int) => format!("{}", int), - }; - - let possibilities = obj_source.data_descriptors(); - - let mut possible_matches: Vec<_> = possibilities - .into_iter() - .map(|x| { - let word = x.clone(); - let distance = natural::distance::levenshtein_distance(&word, &field_tried); - - (distance, word) - }) - .collect(); - - if !possible_matches.is_empty() { - possible_matches.sort(); - Some(possible_matches) - } else { - None - } -} - pub struct AbsoluteFile { inner: PathBuf, }