From 9de0b27867e359b837d1d7126cf84f2a872ee7fb Mon Sep 17 00:00:00 2001 From: George Pollard Date: Sat, 24 Aug 2019 14:12:35 +1200 Subject: [PATCH 01/15] Use Unicode box-drawing characters for tables --- src/format.rs | 1 + src/format/consts.rs | 14 ++++++++++++++ src/format/table.rs | 14 ++------------ src/format/vtable.rs | 12 ++---------- 4 files changed, 19 insertions(+), 22 deletions(-) create mode 100644 src/format/consts.rs diff --git a/src/format.rs b/src/format.rs index ae43c6f8a9..b23b67534e 100644 --- a/src/format.rs +++ b/src/format.rs @@ -1,3 +1,4 @@ +crate mod consts; crate mod entries; crate mod generic; crate mod list; diff --git a/src/format/consts.rs b/src/format/consts.rs new file mode 100644 index 0000000000..c36870da8f --- /dev/null +++ b/src/format/consts.rs @@ -0,0 +1,14 @@ +use lazy_static::lazy_static; + +use prettytable::format::{FormatBuilder, LinePosition, LineSeparator, TableFormat}; + +lazy_static! { + pub(crate) static ref TABLE_FORMAT: TableFormat = + FormatBuilder::new() + .column_separator('│') + .separator(LinePosition::Top, LineSeparator::new('━', '┯', ' ', ' ')) + .separator(LinePosition::Title, LineSeparator::new('─', '┼', ' ', ' ')) + .separator(LinePosition::Bottom, LineSeparator::new('━', '┷', ' ', ' ')) + .padding(1, 1) + .build(); +} diff --git a/src/format/table.rs b/src/format/table.rs index ec641bff0c..c01d24ca4e 100644 --- a/src/format/table.rs +++ b/src/format/table.rs @@ -1,9 +1,8 @@ -use crate::format::RenderView; +use crate::format::{RenderView, consts}; use crate::object::Value; use crate::prelude::*; use ansi_term::Color; use derive_new::new; -use prettytable::format::{FormatBuilder, LinePosition, LineSeparator}; use textwrap::fill; use prettytable::{color, Attr, Cell, Row, Table}; @@ -191,16 +190,7 @@ impl RenderView for TableView { } let mut table = Table::new(); - - let fb = FormatBuilder::new() - .separator(LinePosition::Top, LineSeparator::new('-', '+', ' ', ' ')) - .separator(LinePosition::Bottom, LineSeparator::new('-', '+', ' ', ' ')) - .separator(LinePosition::Title, LineSeparator::new('-', '+', '|', '|')) - .column_separator('|') - .padding(1, 1); - - //table.set_format(*prettytable::format::consts::FORMAT_NO_LINESEP_WITH_TITLE); - table.set_format(fb.build()); + table.set_format(*consts::TABLE_FORMAT); let header: Vec = self .headers diff --git a/src/format/vtable.rs b/src/format/vtable.rs index 30539321a9..533baea8b2 100644 --- a/src/format/vtable.rs +++ b/src/format/vtable.rs @@ -1,8 +1,7 @@ -use crate::format::RenderView; +use crate::format::{RenderView, consts}; use crate::object::Value; use crate::prelude::*; use derive_new::new; -use prettytable::format::{FormatBuilder, LinePosition, LineSeparator}; use prettytable::{color, Attr, Cell, Row, Table}; @@ -47,14 +46,7 @@ impl RenderView for VTableView { } let mut table = Table::new(); - - let fb = FormatBuilder::new() - .separator(LinePosition::Top, LineSeparator::new('-', '+', ' ', ' ')) - .separator(LinePosition::Bottom, LineSeparator::new('-', '+', ' ', ' ')) - .column_separator('|') - .padding(1, 1); - - table.set_format(fb.build()); + table.set_format(*consts::TABLE_FORMAT); for row in &self.entries { table.add_row(Row::new( From 12cedddd689797b92354cd66112d0e9211fff667 Mon Sep 17 00:00:00 2001 From: George Pollard Date: Sat, 24 Aug 2019 15:25:09 +1200 Subject: [PATCH 02/15] Align bytes values in tables --- src/object/base.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/object/base.rs b/src/object/base.rs index ea86386c6c..e3bcae01df 100644 --- a/src/object/base.rs +++ b/src/object/base.rs @@ -94,14 +94,14 @@ impl Primitive { let byte = byte_unit::Byte::from_bytes(*b as u128); if byte.get_bytes() == 0u128 { - return "".to_string(); + return " – ".to_string(); } let byte = byte.get_appropriate_unit(false); match byte.get_unit() { - byte_unit::ByteUnit::B => format!("{}", byte.format(0)), - _ => format!("{}", byte.format(1)), + byte_unit::ByteUnit::B => format!("{:>5} B ", byte.get_value()), + _ => format!("{:>8}", byte.format(1)), } } Primitive::Int(i) => format!("{}", i), From c67d4a6effe7a436cc5e7f4a9640aab21628f828 Mon Sep 17 00:00:00 2001 From: George Pollard Date: Sat, 24 Aug 2019 17:31:50 +1200 Subject: [PATCH 03/15] Rework implementation method --- src/format/table.rs | 44 +++++++++++++++++++++++--------------------- src/object/base.rs | 23 ++++++++++++++++++++--- 2 files changed, 43 insertions(+), 24 deletions(-) diff --git a/src/format/table.rs b/src/format/table.rs index ec641bff0c..a3779960f9 100644 --- a/src/format/table.rs +++ b/src/format/table.rs @@ -10,8 +10,11 @@ use prettytable::{color, Attr, Cell, Row, Table}; #[derive(Debug, new)] pub struct TableView { + // List of header cell values: headers: Vec, - entries: Vec>, + + // List of rows of cells, each containing value and prettytable style-string: + entries: Vec>, } impl TableView { @@ -41,21 +44,26 @@ impl TableView { let mut entries = vec![]; for (idx, value) in values.iter().enumerate() { - let mut row: Vec = match value { + let mut row: Vec<(String, &'static str)> = match value { Tagged { item: Value::Object(..), .. } => headers .iter() .enumerate() - .map(|(i, d)| value.get_data(d).borrow().format_leaf(Some(&headers[i]))) + .map(|(i, d)| { + let data = value.get_data(d); + return (data.borrow().format_leaf(Some(&headers[i])), data.borrow().style_leaf()); + }) .collect(), - x => vec![x.format_leaf(None)], + x => vec![(x.format_leaf(None), x.style_leaf())], }; if values.len() > 1 { - row.insert(0, format!("{}", idx.to_string())); + // Indices are black, bold, right-aligned: + row.insert(0, (format!("{}", idx.to_string()), "Fdbr")); } + entries.push(row); } @@ -66,13 +74,15 @@ impl TableView { } for head in 0..headers.len() { - let mut current_row_max = 0; + let mut current_col_max = 0; for row in 0..values.len() { - if entries[row][head].len() > current_row_max { - current_row_max = entries[row][head].len(); + let value_length = entries[row][head].0.len(); + if value_length > current_col_max { + current_col_max = value_length; } } - max_per_column.push(std::cmp::max(current_row_max, headers[head].len())); + + max_per_column.push(std::cmp::max(current_col_max, headers[head].len())); } // Different platforms want different amounts of buffer, not sure why @@ -88,9 +98,9 @@ impl TableView { entries[row].truncate(max_num_of_columns); } - headers.push("...".to_string()); + headers.push("…".to_string()); for row in 0..entries.len() { - entries[row].push("...".to_string()); + entries[row].push(("…".to_string(), "c")); // ellipsis is centred } } @@ -167,19 +177,11 @@ impl TableView { if max_per_column[head] > max_naive_column_width { headers[head] = fill(&headers[head], max_column_width); for row in 0..entries.len() { - entries[row][head] = fill(&entries[row][head], max_column_width); + entries[row][head].0 = fill(&entries[row][head].0, max_column_width); } } } - // Paint the number column, if it exists - if entries.len() > 1 { - for row in 0..entries.len() { - entries[row][0] = - format!("{}", Color::Black.bold().paint(entries[row][0].to_string())); - } - } - Some(TableView { headers, entries }) } } @@ -215,7 +217,7 @@ impl RenderView for TableView { table.set_titles(Row::new(header)); for row in &self.entries { - table.add_row(Row::new(row.iter().map(|h| Cell::new(h)).collect())); + table.add_row(Row::new(row.iter().map(|(v, s)| Cell::new(v).style_spec(s)).collect())); } table.print_term(&mut *host.out_terminal()).unwrap(); diff --git a/src/object/base.rs b/src/object/base.rs index e3bcae01df..56c63d257b 100644 --- a/src/object/base.rs +++ b/src/object/base.rs @@ -94,14 +94,14 @@ impl Primitive { let byte = byte_unit::Byte::from_bytes(*b as u128); if byte.get_bytes() == 0u128 { - return " – ".to_string(); + return "—".to_string(); } let byte = byte.get_appropriate_unit(false); match byte.get_unit() { - byte_unit::ByteUnit::B => format!("{:>5} B ", byte.get_value()), - _ => format!("{:>8}", byte.format(1)), + byte_unit::ByteUnit::B => format!("{} B ", byte.get_value()), + _ => format!("{}", byte.format(1)), } } Primitive::Int(i) => format!("{}", i), @@ -118,6 +118,16 @@ impl Primitive { Primitive::Date(d) => format!("{}", d.humanize()), } } + + pub fn style(&self) -> &'static str { + match self { + Primitive::Bytes(0) => "c", // centre 'missing' indicator + Primitive::Int(_) | + Primitive::Bytes(_) | + Primitive::Float(_) => "r", + _ => "" + } + } } #[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Clone, new, Serialize)] @@ -459,6 +469,13 @@ impl Value { } } + crate fn style_leaf(&self) -> &'static str { + match self { + Value::Primitive(p) => p.style(), + _ => "" + } + } + #[allow(unused)] crate fn compare(&self, operator: &Operator, other: &Value) -> Result { match operator { From 877bbcd931143f01499ffb617b779736fcb63c28 Mon Sep 17 00:00:00 2001 From: George Pollard Date: Sat, 24 Aug 2019 17:38:32 +1200 Subject: [PATCH 04/15] Remove unused import --- src/format/table.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/format/table.rs b/src/format/table.rs index a3779960f9..32afc5df93 100644 --- a/src/format/table.rs +++ b/src/format/table.rs @@ -1,7 +1,6 @@ use crate::format::RenderView; use crate::object::Value; use crate::prelude::*; -use ansi_term::Color; use derive_new::new; use prettytable::format::{FormatBuilder, LinePosition, LineSeparator}; use textwrap::fill; From cce5b5bb5e539ee50001bdd0c11cba23d49336b2 Mon Sep 17 00:00:00 2001 From: George Pollard Date: Sun, 25 Aug 2019 20:00:04 +1200 Subject: [PATCH 05/15] Revert ellipsis changes --- src/format/table.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/format/table.rs b/src/format/table.rs index 32afc5df93..badc6bbfb4 100644 --- a/src/format/table.rs +++ b/src/format/table.rs @@ -97,9 +97,9 @@ impl TableView { entries[row].truncate(max_num_of_columns); } - headers.push("…".to_string()); + headers.push("...".to_string()); for row in 0..entries.len() { - entries[row].push(("…".to_string(), "c")); // ellipsis is centred + entries[row].push(("...".to_string(), "c")); // ellipsis is centred } } From 033cae2464913d46d997d9b1953396d35b0a03f9 Mon Sep 17 00:00:00 2001 From: Jonathan Turner Date: Mon, 26 Aug 2019 18:42:14 +1200 Subject: [PATCH 06/15] Fix the build --- src/format.rs | 1 - src/format/consts.rs | 14 -------------- src/format/table.rs | 26 +++++++++++++++++++++----- src/format/vtable.rs | 13 +++++++++++-- 4 files changed, 32 insertions(+), 22 deletions(-) delete mode 100644 src/format/consts.rs diff --git a/src/format.rs b/src/format.rs index b23b67534e..ae43c6f8a9 100644 --- a/src/format.rs +++ b/src/format.rs @@ -1,4 +1,3 @@ -crate mod consts; crate mod entries; crate mod generic; crate mod list; diff --git a/src/format/consts.rs b/src/format/consts.rs deleted file mode 100644 index c36870da8f..0000000000 --- a/src/format/consts.rs +++ /dev/null @@ -1,14 +0,0 @@ -use lazy_static::lazy_static; - -use prettytable::format::{FormatBuilder, LinePosition, LineSeparator, TableFormat}; - -lazy_static! { - pub(crate) static ref TABLE_FORMAT: TableFormat = - FormatBuilder::new() - .column_separator('│') - .separator(LinePosition::Top, LineSeparator::new('━', '┯', ' ', ' ')) - .separator(LinePosition::Title, LineSeparator::new('─', '┼', ' ', ' ')) - .separator(LinePosition::Bottom, LineSeparator::new('━', '┷', ' ', ' ')) - .padding(1, 1) - .build(); -} diff --git a/src/format/table.rs b/src/format/table.rs index db126b9d1b..eb6babeae7 100644 --- a/src/format/table.rs +++ b/src/format/table.rs @@ -1,9 +1,10 @@ -use crate::format::{RenderView, consts}; +use crate::format::RenderView; use crate::object::Value; use crate::prelude::*; use derive_new::new; use textwrap::fill; +use prettytable::format::{FormatBuilder, LinePosition, LineSeparator}; use prettytable::{color, Attr, Cell, Row, Table}; #[derive(Debug, new)] @@ -49,9 +50,12 @@ impl TableView { } => headers .iter() .enumerate() - .map(|(i, d)| { + .map(|(i, d)| { let data = value.get_data(d); - return (data.borrow().format_leaf(Some(&headers[i])), data.borrow().style_leaf()); + return ( + data.borrow().format_leaf(Some(&headers[i])), + data.borrow().style_leaf(), + ); }) .collect(), x => vec![(x.format_leaf(None), x.style_leaf())], @@ -191,7 +195,15 @@ impl RenderView for TableView { } let mut table = Table::new(); - table.set_format(*consts::TABLE_FORMAT); + table.set_format( + FormatBuilder::new() + .column_separator('│') + .separator(LinePosition::Top, LineSeparator::new('━', '┯', ' ', ' ')) + .separator(LinePosition::Title, LineSeparator::new('─', '┼', ' ', ' ')) + .separator(LinePosition::Bottom, LineSeparator::new('━', '┷', ' ', ' ')) + .padding(1, 1) + .build(), + ); let header: Vec = self .headers @@ -206,7 +218,11 @@ impl RenderView for TableView { table.set_titles(Row::new(header)); for row in &self.entries { - table.add_row(Row::new(row.iter().map(|(v, s)| Cell::new(v).style_spec(s)).collect())); + table.add_row(Row::new( + row.iter() + .map(|(v, s)| Cell::new(v).style_spec(s)) + .collect(), + )); } table.print_term(&mut *host.out_terminal()).unwrap(); diff --git a/src/format/vtable.rs b/src/format/vtable.rs index 533baea8b2..08c4a72d0d 100644 --- a/src/format/vtable.rs +++ b/src/format/vtable.rs @@ -1,8 +1,9 @@ -use crate::format::{RenderView, consts}; +use crate::format::RenderView; use crate::object::Value; use crate::prelude::*; use derive_new::new; +use prettytable::format::{FormatBuilder, LinePosition, LineSeparator}; use prettytable::{color, Attr, Cell, Row, Table}; #[derive(new)] @@ -46,7 +47,15 @@ impl RenderView for VTableView { } let mut table = Table::new(); - table.set_format(*consts::TABLE_FORMAT); + table.set_format( + FormatBuilder::new() + .column_separator('│') + .separator(LinePosition::Top, LineSeparator::new('━', '┯', ' ', ' ')) + .separator(LinePosition::Title, LineSeparator::new('─', '┼', ' ', ' ')) + .separator(LinePosition::Bottom, LineSeparator::new('━', '┷', ' ', ' ')) + .padding(1, 1) + .build(), + ); for row in &self.entries { table.add_row(Row::new( From bbe7d68659c84fd138cf1476b60e6712d9e0288e Mon Sep 17 00:00:00 2001 From: Odin Dutton Date: Mon, 26 Aug 2019 15:32:45 +1000 Subject: [PATCH 07/15] Return version from clap This is what `nu --version` uses. --- src/commands/version.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/commands/version.rs b/src/commands/version.rs index 06653ae207..e13017ab51 100644 --- a/src/commands/version.rs +++ b/src/commands/version.rs @@ -5,8 +5,6 @@ use crate::parser::registry::Signature; use crate::prelude::*; use indexmap::IndexMap; -const VERSION: &'static str = env!("CARGO_PKG_VERSION"); - pub struct Version; impl WholeStreamCommand for Version { @@ -34,7 +32,7 @@ pub fn date(args: CommandArgs, registry: &CommandRegistry) -> Result Date: Mon, 26 Aug 2019 11:17:47 -0600 Subject: [PATCH 08/15] Permit use of Windows Batch files --- src/cli.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cli.rs b/src/cli.rs index a76ba20845..945e607547 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -91,7 +91,7 @@ fn load_plugin(path: &std::path::Path, context: &mut Context) -> Result<(), Shel fn load_plugins_in_dir(path: &std::path::PathBuf, context: &mut Context) -> Result<(), ShellError> { let re_bin = Regex::new(r"^nu_plugin_[A-Za-z_]+$")?; - let re_exe = Regex::new(r"^nu_plugin_[A-Za-z_]+\.exe$")?; + let re_exe = Regex::new(r"^nu_plugin_[A-Za-z_]+\.(exe|bat)$")?; match std::fs::read_dir(path) { Ok(p) => { From b77effa43469e0cf0ec9302a525c3a0abb466428 Mon Sep 17 00:00:00 2001 From: Dirkjan Ochtman Date: Mon, 26 Aug 2019 20:19:05 +0200 Subject: [PATCH 09/15] Fix formatting with cargo fmt --- src/commands.rs | 2 +- src/commands/from_bson.rs | 108 ++++++++++++++++++----------------- src/commands/last.rs | 2 +- src/commands/open.rs | 24 ++++---- src/commands/sort_by.rs | 4 +- src/git.rs | 14 ++--- src/object/base.rs | 8 +-- src/utils.rs | 8 ++- tests/command_mkdir_tests.rs | 2 +- tests/command_mv_tests.rs | 85 ++++++++------------------- tests/command_open_tests.rs | 2 +- tests/command_rm_tests.rs | 15 ++--- tests/filter_inc_tests.rs | 55 +++++++++--------- 13 files changed, 148 insertions(+), 181 deletions(-) diff --git a/src/commands.rs b/src/commands.rs index bde49d804f..9b17e9db95 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -35,8 +35,8 @@ crate mod pick; crate mod plugin; crate mod prev; crate mod ps; -crate mod reverse; crate mod reject; +crate mod reverse; crate mod rm; crate mod save; crate mod shells; diff --git a/src/commands/from_bson.rs b/src/commands/from_bson.rs index 0f0b8d7cbb..2d98e1907c 100644 --- a/src/commands/from_bson.rs +++ b/src/commands/from_bson.rs @@ -2,7 +2,7 @@ use crate::commands::WholeStreamCommand; use crate::object::base::OF64; use crate::object::{Primitive, TaggedDictBuilder, Value}; use crate::prelude::*; -use bson::{decode_document, Bson, spec::BinarySubtype}; +use bson::{decode_document, spec::BinarySubtype, Bson}; pub struct FromBSON; @@ -47,71 +47,72 @@ fn convert_bson_value_to_nu_value(v: &Bson, tag: impl Into) -> Tagged Value::Primitive(Primitive::Boolean(*b)).tagged(tag), Bson::Null => Value::Primitive(Primitive::String(String::from(""))).tagged(tag), Bson::RegExp(r, opts) => { - let mut collected = TaggedDictBuilder::new(tag); - collected.insert_tagged( - "$regex".to_string(), - Value::Primitive(Primitive::String(String::from(r))).tagged(tag), - ); - collected.insert_tagged( - "$options".to_string(), - Value::Primitive(Primitive::String(String::from(opts))).tagged(tag), - ); - collected.into_tagged_value() + let mut collected = TaggedDictBuilder::new(tag); + collected.insert_tagged( + "$regex".to_string(), + Value::Primitive(Primitive::String(String::from(r))).tagged(tag), + ); + collected.insert_tagged( + "$options".to_string(), + Value::Primitive(Primitive::String(String::from(opts))).tagged(tag), + ); + collected.into_tagged_value() } Bson::I32(n) => Value::Primitive(Primitive::Int(*n as i64)).tagged(tag), Bson::I64(n) => Value::Primitive(Primitive::Int(*n as i64)).tagged(tag), Bson::JavaScriptCode(js) => { - let mut collected = TaggedDictBuilder::new(tag); - collected.insert_tagged( - "$javascript".to_string(), - Value::Primitive(Primitive::String(String::from(js))).tagged(tag), - ); - collected.into_tagged_value() + let mut collected = TaggedDictBuilder::new(tag); + collected.insert_tagged( + "$javascript".to_string(), + Value::Primitive(Primitive::String(String::from(js))).tagged(tag), + ); + collected.into_tagged_value() } Bson::JavaScriptCodeWithScope(js, doc) => { - let mut collected = TaggedDictBuilder::new(tag); - collected.insert_tagged( - "$javascript".to_string(), - Value::Primitive(Primitive::String(String::from(js))).tagged(tag), - ); - collected.insert_tagged( - "$scope".to_string(), - convert_bson_value_to_nu_value(&Bson::Document(doc.to_owned()), tag), - ); - collected.into_tagged_value() + let mut collected = TaggedDictBuilder::new(tag); + collected.insert_tagged( + "$javascript".to_string(), + Value::Primitive(Primitive::String(String::from(js))).tagged(tag), + ); + collected.insert_tagged( + "$scope".to_string(), + convert_bson_value_to_nu_value(&Bson::Document(doc.to_owned()), tag), + ); + collected.into_tagged_value() } Bson::TimeStamp(ts) => { - let mut collected = TaggedDictBuilder::new(tag); - collected.insert_tagged( - "$timestamp".to_string(), - Value::Primitive(Primitive::Int(*ts as i64)).tagged(tag), - ); - collected.into_tagged_value() + let mut collected = TaggedDictBuilder::new(tag); + collected.insert_tagged( + "$timestamp".to_string(), + Value::Primitive(Primitive::Int(*ts as i64)).tagged(tag), + ); + collected.into_tagged_value() } Bson::Binary(bst, bytes) => { - let mut collected = TaggedDictBuilder::new(tag); - collected.insert_tagged( - "$binary_subtype".to_string(), - match bst { - BinarySubtype::UserDefined(u) => Value::Primitive(Primitive::Int(*u as i64)), - _ => Value::Primitive(Primitive::String(binary_subtype_to_string(*bst))), - }.tagged(tag) - ); - collected.insert_tagged( - "$binary".to_string(), - Value::Binary(bytes.to_owned()).tagged(tag), - ); - collected.into_tagged_value() + let mut collected = TaggedDictBuilder::new(tag); + collected.insert_tagged( + "$binary_subtype".to_string(), + match bst { + BinarySubtype::UserDefined(u) => Value::Primitive(Primitive::Int(*u as i64)), + _ => Value::Primitive(Primitive::String(binary_subtype_to_string(*bst))), + } + .tagged(tag), + ); + collected.insert_tagged( + "$binary".to_string(), + Value::Binary(bytes.to_owned()).tagged(tag), + ); + collected.into_tagged_value() } Bson::ObjectId(obj_id) => Value::Primitive(Primitive::String(obj_id.to_hex())).tagged(tag), Bson::UtcDatetime(dt) => Value::Primitive(Primitive::Date(*dt)).tagged(tag), Bson::Symbol(s) => { - let mut collected = TaggedDictBuilder::new(tag); - collected.insert_tagged( - "$symbol".to_string(), - Value::Primitive(Primitive::String(String::from(s))).tagged(tag), - ); - collected.into_tagged_value() + let mut collected = TaggedDictBuilder::new(tag); + collected.insert_tagged( + "$symbol".to_string(), + Value::Primitive(Primitive::String(String::from(s))).tagged(tag), + ); + collected.into_tagged_value() } } } @@ -125,7 +126,8 @@ fn binary_subtype_to_string(bst: BinarySubtype) -> String { BinarySubtype::Uuid => "uuid", BinarySubtype::Md5 => "md5", _ => unreachable!(), - }.to_string() + } + .to_string() } #[derive(Debug)] diff --git a/src/commands/last.rs b/src/commands/last.rs index 18a2da96a5..f776b7d654 100644 --- a/src/commands/last.rs +++ b/src/commands/last.rs @@ -44,7 +44,7 @@ fn last(args: CommandArgs, registry: &CommandRegistry) -> Result Result, ShellError> { match extension { - Some(x) if x == "csv" => crate::commands::from_csv::from_csv_string_to_value( - contents, - false, - contents_tag, - ) - .map_err(move |_| { - ShellError::labeled_error("Could not open as CSV", "could not open as CSV", name_span) - }), + Some(x) if x == "csv" => { + crate::commands::from_csv::from_csv_string_to_value(contents, false, contents_tag) + .map_err(move |_| { + ShellError::labeled_error( + "Could not open as CSV", + "could not open as CSV", + name_span, + ) + }) + } Some(x) if x == "toml" => { crate::commands::from_toml::from_toml_string_to_value(contents, contents_tag).map_err( move |_| { @@ -507,9 +509,9 @@ pub fn parse_binary_as_value( crate::commands::from_bson::from_bson_bytes_to_value(contents, contents_tag).map_err( move |_| { ShellError::labeled_error( - "Could not open as BSON", - "could not open as BSON", - name_span, + "Could not open as BSON", + "could not open as BSON", + name_span, ) }, ) diff --git a/src/commands/sort_by.rs b/src/commands/sort_by.rs index 820e0a471d..1a8d74b03c 100644 --- a/src/commands/sort_by.rs +++ b/src/commands/sort_by.rs @@ -46,9 +46,7 @@ fn sort_by(args: CommandArgs, registry: &CommandRegistry) -> Result>>>() }; if reverse { - vec.sort_by_cached_key(|item| { - std::cmp::Reverse(calc_key(item)) - }); + vec.sort_by_cached_key(|item| std::cmp::Reverse(calc_key(item))); } else { vec.sort_by_cached_key(calc_key); } diff --git a/src/git.rs b/src/git.rs index 4782765a23..11b37ab5db 100644 --- a/src/git.rs +++ b/src/git.rs @@ -7,15 +7,13 @@ pub fn current_branch() -> Option { Ok(repo) => { let r = repo.head(); match r { - Ok(r) => { - match r.shorthand() { - Some(s) => Some(s.to_string()), - None => None, - } + Ok(r) => match r.shorthand() { + Some(s) => Some(s.to_string()), + None => None, }, - _ => None + _ => None, } - }, - _ => None + } + _ => None, } } diff --git a/src/object/base.rs b/src/object/base.rs index 56c63d257b..612b8f5293 100644 --- a/src/object/base.rs +++ b/src/object/base.rs @@ -122,10 +122,8 @@ impl Primitive { pub fn style(&self) -> &'static str { match self { Primitive::Bytes(0) => "c", // centre 'missing' indicator - Primitive::Int(_) | - Primitive::Bytes(_) | - Primitive::Float(_) => "r", - _ => "" + Primitive::Int(_) | Primitive::Bytes(_) | Primitive::Float(_) => "r", + _ => "", } } } @@ -472,7 +470,7 @@ impl Value { crate fn style_leaf(&self) -> &'static str { match self { Value::Primitive(p) => p.style(), - _ => "" + _ => "", } } diff --git a/src/utils.rs b/src/utils.rs index 99a59e850e..7a0527565e 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -105,7 +105,10 @@ impl FileStructure { self.root = path.to_path_buf(); } - pub fn paths_applying_with(&mut self, to: F) -> Result, Box> + pub fn paths_applying_with( + &mut self, + to: F, + ) -> Result, Box> where F: Fn((PathBuf, usize)) -> Result<(PathBuf, PathBuf), Box>, { @@ -175,7 +178,8 @@ mod tests { fn prepares_and_decorates_source_files_for_copying() { let mut res = FileStructure::new(); - res.walk_decorate(fixtures().as_path()).expect("Can not decorate files traversal."); + res.walk_decorate(fixtures().as_path()) + .expect("Can not decorate files traversal."); assert_eq!( res.resources, diff --git a/tests/command_mkdir_tests.rs b/tests/command_mkdir_tests.rs index b8564a726e..fcc88ee9fe 100644 --- a/tests/command_mkdir_tests.rs +++ b/tests/command_mkdir_tests.rs @@ -25,7 +25,7 @@ fn accepts_and_creates_directories() { let full_path = format!("{}/{}", Playground::root(), sandbox); nu!(_output, cwd(&full_path), "mkdir dir_1 dir_2 dir_3"); - + assert!(h::files_exist_at( vec![Path::new("dir_1"), Path::new("dir_2"), Path::new("dir_3")], PathBuf::from(&full_path) diff --git a/tests/command_mv_tests.rs b/tests/command_mv_tests.rs index 62132c7f2a..dc4c1a25fa 100644 --- a/tests/command_mv_tests.rs +++ b/tests/command_mv_tests.rs @@ -8,15 +8,13 @@ use std::path::{Path, PathBuf}; #[test] fn moves_a_file() { let sandbox = Playground::setup_for("mv_test_1") - .with_files(vec![ - EmptyFile("andres.txt"), - ]) + .with_files(vec![EmptyFile("andres.txt")]) .mkdir("expected") .test_dir_name(); let full_path = format!("{}/{}", Playground::root(), sandbox); let original = format!("{}/{}", full_path, "andres.txt"); - let expected = format!("{}/{}", full_path, "expected/yehuda.txt"); + let expected = format!("{}/{}", full_path, "expected/yehuda.txt"); nu!( _output, @@ -31,21 +29,14 @@ fn moves_a_file() { #[test] fn overwrites_if_moving_to_existing_file() { let sandbox = Playground::setup_for("mv_test_2") - .with_files(vec![ - EmptyFile("andres.txt"), - EmptyFile("jonathan.txt"), - ]) + .with_files(vec![EmptyFile("andres.txt"), EmptyFile("jonathan.txt")]) .test_dir_name(); let full_path = format!("{}/{}", Playground::root(), sandbox); let original = format!("{}/{}", full_path, "andres.txt"); - let expected = format!("{}/{}", full_path, "jonathan.txt"); + let expected = format!("{}/{}", full_path, "jonathan.txt"); - nu!( - _output, - cwd(&full_path), - "mv andres.txt jonathan.txt" - ); + nu!(_output, cwd(&full_path), "mv andres.txt jonathan.txt"); assert!(!h::file_exists_at(PathBuf::from(original))); assert!(h::file_exists_at(PathBuf::from(expected))); @@ -58,14 +49,10 @@ fn moves_a_directory() { .test_dir_name(); let full_path = format!("{}/{}", Playground::root(), sandbox); - let original_dir = format!("{}/{}", full_path, "empty_dir"); - let expected = format!("{}/{}", full_path, "renamed_dir"); + let original_dir = format!("{}/{}", full_path, "empty_dir"); + let expected = format!("{}/{}", full_path, "renamed_dir"); - nu!( - _output, - cwd(&full_path), - "mv empty_dir renamed_dir" - ); + nu!(_output, cwd(&full_path), "mv empty_dir renamed_dir"); assert!(!h::dir_exists_at(PathBuf::from(original_dir))); assert!(h::dir_exists_at(PathBuf::from(expected))); @@ -74,22 +61,15 @@ fn moves_a_directory() { #[test] fn moves_the_file_inside_directory_if_path_to_move_is_existing_directory() { let sandbox = Playground::setup_for("mv_test_4") - .with_files(vec![ - EmptyFile("jonathan.txt"), - ]) + .with_files(vec![EmptyFile("jonathan.txt")]) .mkdir("expected") .test_dir_name(); let full_path = format!("{}/{}", Playground::root(), sandbox); - let original_dir = format!("{}/{}", full_path, "jonathan.txt"); - let expected = format!("{}/{}", full_path, "expected/jonathan.txt"); - - nu!( - _output, - cwd(&full_path), - "mv jonathan.txt expected" - ); + let original_dir = format!("{}/{}", full_path, "jonathan.txt"); + let expected = format!("{}/{}", full_path, "expected/jonathan.txt"); + nu!(_output, cwd(&full_path), "mv jonathan.txt expected"); assert!(!h::file_exists_at(PathBuf::from(original_dir))); assert!(h::file_exists_at(PathBuf::from(expected))); @@ -99,22 +79,15 @@ fn moves_the_file_inside_directory_if_path_to_move_is_existing_directory() { fn moves_the_directory_inside_directory_if_path_to_move_is_existing_directory() { let sandbox = Playground::setup_for("mv_test_5") .within("contributors") - .with_files(vec![ - EmptyFile("jonathan.txt"), - ]) + .with_files(vec![EmptyFile("jonathan.txt")]) .mkdir("expected") .test_dir_name(); let full_path = format!("{}/{}", Playground::root(), sandbox); - let original_dir = format!("{}/{}", full_path, "contributors"); - let expected = format!("{}/{}", full_path, "expected/contributors"); - - nu!( - _output, - cwd(&full_path), - "mv contributors expected" - ); + let original_dir = format!("{}/{}", full_path, "contributors"); + let expected = format!("{}/{}", full_path, "expected/contributors"); + nu!(_output, cwd(&full_path), "mv contributors expected"); assert!(!h::dir_exists_at(PathBuf::from(original_dir))); assert!(h::file_exists_at(PathBuf::from(expected))); @@ -124,14 +97,12 @@ fn moves_the_directory_inside_directory_if_path_to_move_is_existing_directory() fn moves_the_directory_inside_directory_if_path_to_move_is_nonexistent_directory() { let sandbox = Playground::setup_for("mv_test_6") .within("contributors") - .with_files(vec![ - EmptyFile("jonathan.txt"), - ]) + .with_files(vec![EmptyFile("jonathan.txt")]) .mkdir("expected") .test_dir_name(); let full_path = format!("{}/{}", Playground::root(), sandbox); - let original_dir = format!("{}/{}", full_path, "contributors"); + let original_dir = format!("{}/{}", full_path, "contributors"); nu!( _output, @@ -139,7 +110,10 @@ fn moves_the_directory_inside_directory_if_path_to_move_is_nonexistent_directory "mv contributors expected/this_dir_exists_now/los_tres_amigos" ); - let expected = format!("{}/{}", full_path, "expected/this_dir_exists_now/los_tres_amigos"); + let expected = format!( + "{}/{}", + full_path, "expected/this_dir_exists_now/los_tres_amigos" + ); assert!(!h::dir_exists_at(PathBuf::from(original_dir))); assert!(h::file_exists_at(PathBuf::from(expected))); @@ -168,11 +142,7 @@ fn moves_using_path_with_wildcard() { let work_dir = format!("{}/{}", full_path, "work_dir"); let expected_copies_path = format!("{}/{}", full_path, "expected"); - nu!( - _output, - cwd(&work_dir), - "mv ../originals/*.ini ../expected" - ); + nu!(_output, cwd(&work_dir), "mv ../originals/*.ini ../expected"); assert!(h::files_exist_at( vec![ @@ -185,7 +155,6 @@ fn moves_using_path_with_wildcard() { )); } - #[test] fn moves_using_a_glob() { let sandbox = Playground::setup_for("mv_test_8") @@ -204,11 +173,7 @@ fn moves_using_a_glob() { let work_dir = format!("{}/{}", full_path, "work_dir"); let expected_copies_path = format!("{}/{}", full_path, "expected"); - nu!( - _output, - cwd(&work_dir), - "mv ../meals/* ../expected" - ); + nu!(_output, cwd(&work_dir), "mv ../meals/* ../expected"); assert!(h::dir_exists_at(PathBuf::from(meal_dir))); assert!(h::files_exist_at( @@ -219,4 +184,4 @@ fn moves_using_a_glob() { ], PathBuf::from(&expected_copies_path) )); -} \ No newline at end of file +} diff --git a/tests/command_open_tests.rs b/tests/command_open_tests.rs index abcd216e1a..bf33ec63f1 100644 --- a/tests/command_open_tests.rs +++ b/tests/command_open_tests.rs @@ -111,4 +111,4 @@ fn errors_if_file_not_found() { ); assert!(output.contains("File could not be opened")); -} \ No newline at end of file +} diff --git a/tests/command_rm_tests.rs b/tests/command_rm_tests.rs index f535100836..76e7128208 100644 --- a/tests/command_rm_tests.rs +++ b/tests/command_rm_tests.rs @@ -90,7 +90,7 @@ fn rm_removes_deeply_nested_directories_with_wildcard_and_recursive_flag() { .test_dir_name(); let full_path = format!("{}/{}", Playground::root(), sandbox); - + nu!( _output, cwd("tests/fixtures/nuplayground/rm_wildcard_test_2"), @@ -98,10 +98,7 @@ fn rm_removes_deeply_nested_directories_with_wildcard_and_recursive_flag() { ); assert!(!h::files_exist_at( - vec![ - Path::new("src/parser/parse"), - Path::new("src/parser/hir"), - ], + vec![Path::new("src/parser/parse"), Path::new("src/parser/hir"),], PathBuf::from(&full_path) )); } @@ -150,7 +147,11 @@ fn rm_errors_if_attempting_to_delete_a_directory_with_content_without_recursive_ let full_path = format!("{}/{}", Playground::root(), sandbox); - nu_error!(output, cwd(&Playground::root()), "rm rm_prevent_directory_removal_without_flag_test"); + nu_error!( + output, + cwd(&Playground::root()), + "rm rm_prevent_directory_removal_without_flag_test" + ); assert!(h::file_exists_at(PathBuf::from(full_path))); assert!(output.contains("is a directory")); @@ -168,4 +169,4 @@ fn rm_errors_if_attempting_to_delete_two_dot_as_argument() { nu_error!(output, cwd(&Playground::root()), "rm .."); assert!(output.contains("may not be removed")); -} \ No newline at end of file +} diff --git a/tests/filter_inc_tests.rs b/tests/filter_inc_tests.rs index 430c3076c1..449380961b 100644 --- a/tests/filter_inc_tests.rs +++ b/tests/filter_inc_tests.rs @@ -16,14 +16,15 @@ fn can_only_apply_one() { #[test] fn by_one_with_field_passed() { - Playground::setup_for("plugin_inc_by_one_with_field_passed_test") - .with_files(vec![FileWithContent( + Playground::setup_for("plugin_inc_by_one_with_field_passed_test").with_files(vec![ + FileWithContent( "sample.toml", r#" [package] edition = "2018" "#, - )]); + ), + ]); nu!( output, @@ -36,35 +37,34 @@ fn by_one_with_field_passed() { #[test] fn by_one_with_no_field_passed() { - Playground::setup_for("plugin_inc_by_one_with_no_field_passed_test") - .with_files(vec![FileWithContent( + Playground::setup_for("plugin_inc_by_one_with_no_field_passed_test").with_files(vec![ + FileWithContent( "sample.toml", r#" [package] contributors = "2" "#, - )]); - + ), + ]); + nu!( output, cwd("tests/fixtures/nuplayground/plugin_inc_by_one_with_no_field_passed_test"), "open sample.toml | get package.contributors | inc | echo $it" ); - + assert_eq!(output, "3"); } - #[test] fn semversion_major_inc() { - Playground::setup_for("plugin_inc_major_semversion_test") - .with_files(vec![FileWithContent( - "sample.toml", - r#" + Playground::setup_for("plugin_inc_major_semversion_test").with_files(vec![FileWithContent( + "sample.toml", + r#" [package] version = "0.1.3" "#, - )]); + )]); nu!( output, @@ -77,14 +77,13 @@ fn semversion_major_inc() { #[test] fn semversion_minor_inc() { - Playground::setup_for("plugin_inc_minor_semversion_test") - .with_files(vec![FileWithContent( - "sample.toml", - r#" + Playground::setup_for("plugin_inc_minor_semversion_test").with_files(vec![FileWithContent( + "sample.toml", + r#" [package] version = "0.1.3" "#, - )]); + )]); nu!( output, @@ -97,14 +96,13 @@ fn semversion_minor_inc() { #[test] fn semversion_patch_inc() { - Playground::setup_for("plugin_inc_patch_semversion_test") - .with_files(vec![FileWithContent( - "sample.toml", - r#" + Playground::setup_for("plugin_inc_patch_semversion_test").with_files(vec![FileWithContent( + "sample.toml", + r#" [package] version = "0.1.3" "#, - )]); + )]); nu!( output, @@ -117,14 +115,15 @@ fn semversion_patch_inc() { #[test] fn semversion_without_passing_field() { - Playground::setup_for("plugin_inc_semversion_without_passing_field_test") - .with_files(vec![FileWithContent( + Playground::setup_for("plugin_inc_semversion_without_passing_field_test").with_files(vec![ + FileWithContent( "sample.toml", r#" [package] version = "0.1.3" "#, - )]); + ), + ]); nu!( output, @@ -133,4 +132,4 @@ fn semversion_without_passing_field() { ); assert_eq!(output, "0.1.4"); -} \ No newline at end of file +} From ce0113eb19720d14dfeddc5fc2052c500858c3b9 Mon Sep 17 00:00:00 2001 From: Dirkjan Ochtman Date: Mon, 26 Aug 2019 21:26:10 +0200 Subject: [PATCH 10/15] Replace use of unstable Option::flatten() with and_then() --- src/commands/save.rs | 2 +- src/commands/tags.rs | 2 +- src/lib.rs | 1 - src/plugins/binaryview.rs | 3 +-- src/plugins/textview.rs | 4 +--- 5 files changed, 4 insertions(+), 8 deletions(-) diff --git a/src/commands/save.rs b/src/commands/save.rs index 37882f4297..da5c093502 100644 --- a/src/commands/save.rs +++ b/src/commands/save.rs @@ -59,7 +59,7 @@ fn save( // If there is no filename, check the metadata for the origin filename if input.len() > 0 { let origin = input[0].origin(); - match origin.map(|x| source_map.get(&x)).flatten() { + match origin.and_then(|x| source_map.get(&x)) { Some(path) => match path { SpanSource::File(file) => { full_path.push(Path::new(file)); diff --git a/src/commands/tags.rs b/src/commands/tags.rs index b916c03f8c..01c7565d55 100644 --- a/src/commands/tags.rs +++ b/src/commands/tags.rs @@ -38,7 +38,7 @@ fn tags(args: CommandArgs, _registry: &CommandRegistry) -> Result { tags.insert("origin", Value::string(source)); } diff --git a/src/lib.rs b/src/lib.rs index a9ef8740f4..cead5e2f83 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,7 +3,6 @@ #![feature(generators)] #![feature(try_trait)] #![feature(bind_by_move_pattern_guards)] -#![feature(option_flattening)] #![feature(specialization)] #![feature(proc_macro_hygiene)] diff --git a/src/plugins/binaryview.rs b/src/plugins/binaryview.rs index 5668fa8696..58ae9756a8 100644 --- a/src/plugins/binaryview.rs +++ b/src/plugins/binaryview.rs @@ -1,4 +1,3 @@ -#![feature(option_flattening)] use crossterm::{cursor, terminal, Attribute, RawScreen}; use indexmap::IndexMap; use nu::{ @@ -32,7 +31,7 @@ impl Plugin for BinaryView { let value_origin = v.origin(); match v.item { Value::Binary(b) => { - let source = value_origin.map(|x| call_info.source_map.get(&x)).flatten(); + let source = value_origin.and_then(|x| call_info.source_map.get(&x)); let _ = view_binary(&b, source, call_info.args.has("lores")); } _ => {} diff --git a/src/plugins/textview.rs b/src/plugins/textview.rs index 92d95c83e1..238144cbe2 100644 --- a/src/plugins/textview.rs +++ b/src/plugins/textview.rs @@ -1,5 +1,3 @@ -#![feature(option_flattening)] - use crossterm::{cursor, terminal, RawScreen}; use crossterm::{InputEvent, KeyEvent}; use indexmap::IndexMap; @@ -217,7 +215,7 @@ fn view_text_value(value: &Tagged, source_map: &SourceMap) { let value_origin = value.origin(); match value.item { Value::Primitive(Primitive::String(ref s)) => { - let source = value_origin.map(|x| source_map.get(&x)).flatten(); + let source = value_origin.and_then(|x| source_map.get(&x)); if let Some(source) = source { let extension: Option = match source { From 91093f2ab256a7d149098803b0c311b30ce18f11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s=20N=2E=20Robalino?= Date: Mon, 26 Aug 2019 17:16:39 -0500 Subject: [PATCH 11/15] Avoid panicking if history can't be saved. --- src/cli.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cli.rs b/src/cli.rs index 945e607547..fa3a275c27 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -297,7 +297,7 @@ pub async fn cli() -> Result<(), Box> { } ctrlcbreak = false; } - rl.save_history("history.txt")?; + let _ = rl.save_history("history.txt"); Ok(()) } From 3e699db57c58ce722fd20ca3fc8492a5de6723f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s=20N=2E=20Robalino?= Date: Mon, 26 Aug 2019 17:41:57 -0500 Subject: [PATCH 12/15] Aviso. --- src/cli.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/cli.rs b/src/cli.rs index fa3a275c27..614ad28f96 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -217,6 +217,7 @@ pub async fn cli() -> Result<(), Box> { let _ = ansi_term::enable_ansi_support(); } + // we are ok if history does not exist let _ = rl.load_history("history.txt"); let ctrl_c = Arc::new(AtomicBool::new(false)); @@ -297,6 +298,8 @@ pub async fn cli() -> Result<(), Box> { } ctrlcbreak = false; } + + // we are ok if we can not save history let _ = rl.save_history("history.txt"); Ok(()) From 87a99bbabfdb4cc151c07857c999929df1fd57b5 Mon Sep 17 00:00:00 2001 From: Patrick Meredith Date: Mon, 26 Aug 2019 10:16:34 -0400 Subject: [PATCH 13/15] Implement to-bson --- src/cli.rs | 1 + src/commands.rs | 2 + src/commands/from_bson.rs | 10 +- src/commands/to_bson.rs | 231 ++++++++++++++++++++++++++++++++++++++ src/object/base.rs | 42 +++++++ tests/filters_test.rs | 11 ++ 6 files changed, 296 insertions(+), 1 deletion(-) create mode 100644 src/commands/to_bson.rs diff --git a/src/cli.rs b/src/cli.rs index 614ad28f96..b59f5634ad 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -162,6 +162,7 @@ pub async fn cli() -> Result<(), Box> { whole_stream_command(Reverse), whole_stream_command(Trim), whole_stream_command(ToArray), + whole_stream_command(ToBSON), whole_stream_command(ToCSV), whole_stream_command(ToJSON), whole_stream_command(ToTOML), diff --git a/src/commands.rs b/src/commands.rs index 9b17e9db95..d1d9297fd4 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -48,6 +48,7 @@ crate mod split_row; crate mod table; crate mod tags; crate mod to_array; +crate mod to_bson; crate mod to_csv; crate mod to_json; crate mod to_toml; @@ -104,6 +105,7 @@ crate use split_row::SplitRow; crate use table::Table; crate use tags::Tags; crate use to_array::ToArray; +crate use to_bson::ToBSON; crate use to_csv::ToCSV; crate use to_json::ToJSON; crate use to_toml::ToTOML; diff --git a/src/commands/from_bson.rs b/src/commands/from_bson.rs index 2d98e1907c..e244614ccf 100644 --- a/src/commands/from_bson.rs +++ b/src/commands/from_bson.rs @@ -58,6 +58,7 @@ fn convert_bson_value_to_nu_value(v: &Bson, tag: impl Into) -> Tagged Value::Primitive(Primitive::Int(*n as i64)).tagged(tag), Bson::I64(n) => Value::Primitive(Primitive::Int(*n as i64)).tagged(tag), Bson::JavaScriptCode(js) => { @@ -104,7 +105,14 @@ fn convert_bson_value_to_nu_value(v: &Bson, tag: impl Into) -> Tagged Value::Primitive(Primitive::String(obj_id.to_hex())).tagged(tag), + Bson::ObjectId(obj_id) => { + let mut collected = TaggedDictBuilder::new(tag); + collected.insert_tagged( + "$object_id".to_string(), + Value::Primitive(Primitive::String(obj_id.to_hex())).tagged(tag), + ); + collected.into_tagged_value() + } Bson::UtcDatetime(dt) => Value::Primitive(Primitive::Date(*dt)).tagged(tag), Bson::Symbol(s) => { let mut collected = TaggedDictBuilder::new(tag); diff --git a/src/commands/to_bson.rs b/src/commands/to_bson.rs new file mode 100644 index 0000000000..60dc1cf2c8 --- /dev/null +++ b/src/commands/to_bson.rs @@ -0,0 +1,231 @@ +use crate::commands::WholeStreamCommand; +use crate::object::{Dictionary, Primitive, Value}; +use crate::prelude::*; +use bson::{encode_document, oid::ObjectId, spec::BinarySubtype, Bson, Document}; +use std::convert::TryInto; + +pub struct ToBSON; + +impl WholeStreamCommand for ToBSON { + fn run( + &self, + args: CommandArgs, + registry: &CommandRegistry, + ) -> Result { + to_bson(args, registry) + } + + fn name(&self) -> &str { + "to-bson" + } + + fn signature(&self) -> Signature { + Signature::build("to-bson") + } +} + +pub fn value_to_bson_value(v: &Value) -> Bson { + match v { + Value::Primitive(Primitive::Boolean(b)) => Bson::Boolean(*b), + Value::Primitive(Primitive::Bytes(b)) => Bson::I64(*b as i64), + Value::Primitive(Primitive::Date(d)) => Bson::UtcDatetime(*d), + Value::Primitive(Primitive::EndOfStream) => Bson::Null, + Value::Primitive(Primitive::BeginningOfStream) => Bson::Null, + Value::Primitive(Primitive::Float(f)) => Bson::FloatingPoint(f.into_inner()), + Value::Primitive(Primitive::Int(i)) => Bson::I64(*i), + Value::Primitive(Primitive::Nothing) => Bson::Null, + Value::Primitive(Primitive::String(s)) => Bson::String(s.clone()), + Value::Primitive(Primitive::Path(s)) => Bson::String(s.display().to_string()), + Value::List(l) => Bson::Array(l.iter().map(|x| value_to_bson_value(x)).collect()), + Value::Block(_) => Bson::Null, + Value::Binary(b) => Bson::Binary(BinarySubtype::Generic, b.clone()), + Value::Object(o) => object_value_to_bson(o), + } +} + +// object_value_to_bson handles all Objects, even those that correspond to special +// types (things like regex or javascript code). +fn object_value_to_bson(o: &Dictionary) -> Bson { + let mut it = o.entries.iter(); + if it.len() > 2 { + return generic_object_value_to_bson(o); + } + match it.next() { + Some((regex, tagged_regex_value)) if regex == "$regex" => match it.next() { + Some((options, tagged_opts_value)) if options == "$options" => { + let r: Result = tagged_regex_value.try_into(); + let opts: Result = tagged_opts_value.try_into(); + if r.is_err() || opts.is_err() { + generic_object_value_to_bson(o) + } else { + Bson::RegExp(r.unwrap(), opts.unwrap()) + } + } + _ => generic_object_value_to_bson(o), + }, + Some((javascript, tagged_javascript_value)) if javascript == "$javascript" => { + match it.next() { + Some((scope, tagged_scope_value)) if scope == "$scope" => { + let js: Result = tagged_javascript_value.try_into(); + let s: Result<&Dictionary, _> = tagged_scope_value.try_into(); + if js.is_err() || s.is_err() { + generic_object_value_to_bson(o) + } else { + if let Bson::Document(doc) = object_value_to_bson(s.unwrap()) { + Bson::JavaScriptCodeWithScope(js.unwrap(), doc) + } else { + generic_object_value_to_bson(o) + } + } + } + None => { + let js: Result = tagged_javascript_value.try_into(); + if js.is_err() { + generic_object_value_to_bson(o) + } else { + Bson::JavaScriptCode(js.unwrap()) + } + } + _ => generic_object_value_to_bson(o), + } + } + Some((timestamp, tagged_timestamp_value)) if timestamp == "$timestamp" => { + let ts: Result = tagged_timestamp_value.try_into(); + if ts.is_err() { + generic_object_value_to_bson(o) + } else { + Bson::TimeStamp(ts.unwrap()) + } + } + Some((binary_subtype, tagged_binary_subtype_value)) + if binary_subtype == "$binary_subtype" => + { + match it.next() { + Some((binary, tagged_bin_value)) if binary == "$binary" => { + let bst = get_binary_subtype(tagged_binary_subtype_value); + let bin: Result, _> = tagged_bin_value.try_into(); + if bst.is_none() || bin.is_err() { + generic_object_value_to_bson(o) + } else { + Bson::Binary(bst.unwrap(), bin.unwrap()) + } + } + _ => generic_object_value_to_bson(o), + } + } + Some((object_id, tagged_object_id_value)) if object_id == "$object_id" => { + let obj_id: Result = tagged_object_id_value.try_into(); + if obj_id.is_err() { + generic_object_value_to_bson(o) + } else { + let obj_id = ObjectId::with_string(&obj_id.unwrap()); + if obj_id.is_err() { + generic_object_value_to_bson(o) + } else { + Bson::ObjectId(obj_id.unwrap()) + } + } + } + Some((symbol, tagged_symbol_value)) if symbol == "$symbol" => { + let sym: Result = tagged_symbol_value.try_into(); + if sym.is_err() { + generic_object_value_to_bson(o) + } else { + Bson::Symbol(sym.unwrap()) + } + } + _ => generic_object_value_to_bson(o), + } +} + +fn get_binary_subtype<'a>(tagged_value: &'a Tagged) -> Option { + match tagged_value.item() { + Value::Primitive(Primitive::String(s)) => Some(match s.as_ref() { + "generic" => BinarySubtype::Generic, + "function" => BinarySubtype::Function, + "binary_old" => BinarySubtype::BinaryOld, + "uuid_old" => BinarySubtype::UuidOld, + "uuid" => BinarySubtype::Uuid, + "md5" => BinarySubtype::Md5, + _ => unreachable!(), + }), + Value::Primitive(Primitive::Int(i)) => Some(BinarySubtype::UserDefined(*i as u8)), + _ => None, + } +} + +// generic_object_value_bson handles any Object that does not +// correspond to a special bson type (things like regex or javascript code). +fn generic_object_value_to_bson(o: &Dictionary) -> Bson { + let mut doc = Document::new(); + for (k, v) in o.entries.iter() { + doc.insert(k.clone(), value_to_bson_value(v)); + } + Bson::Document(doc) +} + +fn shell_encode_document( + writer: &mut Vec, + doc: Document, + span: Span, +) -> Result<(), ShellError> { + match encode_document(writer, &doc) { + Err(e) => Err(ShellError::labeled_error( + format!("Failed to encode document due to: {:?}", e), + "requires BSON-compatible document", + span, + )), + _ => Ok(()), + } +} + +fn bson_value_to_bytes(bson: Bson, span: Span) -> Result, ShellError> { + let mut out = Vec::new(); + match bson { + Bson::Array(a) => { + for v in a.into_iter() { + match v { + Bson::Document(d) => shell_encode_document(&mut out, d, span)?, + _ => { + return Err(ShellError::labeled_error( + format!("All top level values must be Documents, got {:?}", v), + "requires BSON-compatible document", + span, + )) + } + } + } + } + Bson::Document(d) => shell_encode_document(&mut out, d, span)?, + _ => { + return Err(ShellError::labeled_error( + format!("All top level values must be Documents, got {:?}", bson), + "requires BSON-compatible document", + span, + )) + } + } + Ok(out) +} + +fn to_bson(args: CommandArgs, registry: &CommandRegistry) -> Result { + let args = args.evaluate_once(registry)?; + let name_span = args.name_span(); + let out = args.input; + + Ok(out + .values + .map( + move |a| match bson_value_to_bytes(value_to_bson_value(&a), name_span) { + Ok(x) => ReturnSuccess::value(Value::Binary(x).simple_spanned(name_span)), + _ => Err(ShellError::labeled_error_with_secondary( + "Expected an object with BSON-compatible structure from pipeline", + "requires BSON-compatible input: Must be Array or Object", + name_span, + format!("{} originates from here", a.item.type_name()), + a.span(), + )), + }, + ) + .to_output_stream()) +} diff --git a/src/object/base.rs b/src/object/base.rs index 612b8f5293..5b136b3d4c 100644 --- a/src/object/base.rs +++ b/src/object/base.rs @@ -243,6 +243,48 @@ impl std::convert::TryFrom<&'a Tagged> for i64 { } } +impl std::convert::TryFrom<&'a Tagged> for String { + type Error = ShellError; + + fn try_from(value: &'a Tagged) -> Result { + match value.item() { + Value::Primitive(Primitive::String(s)) => Ok(s.clone()), + v => Err(ShellError::type_error( + "String", + value.copy_span(v.type_name()), + )), + } + } +} + +impl std::convert::TryFrom<&'a Tagged> for Vec { + type Error = ShellError; + + fn try_from(value: &'a Tagged) -> Result, ShellError> { + match value.item() { + Value::Binary(b) => Ok(b.clone()), + v => Err(ShellError::type_error( + "Binary", + value.copy_span(v.type_name()), + )), + } + } +} + +impl std::convert::TryFrom<&'a Tagged> for &'a crate::object::Dictionary { + type Error = ShellError; + + fn try_from(value: &'a Tagged) -> Result<&'a crate::object::Dictionary, ShellError> { + match value.item() { + Value::Object(d) => Ok(d), + v => Err(ShellError::type_error( + "Dictionary", + value.copy_span(v.type_name()), + )), + } + } +} + #[derive(Serialize, Deserialize)] pub enum Switch { Present, diff --git a/tests/filters_test.rs b/tests/filters_test.rs index 7baca493a3..ac68ea5fff 100644 --- a/tests/filters_test.rs +++ b/tests/filters_test.rs @@ -106,6 +106,17 @@ fn can_convert_table_to_json_text_and_from_json_text_back_into_table() { assert_eq!(output, "markup"); } +#[test] +fn can_convert_json_text_to_bson_and_back_into_table() { + nu!( + output, + cwd("tests/fixtures/formats"), + "echo '{\"root\":[{\"x\": 2, \"y\": 4}, {\"z\": \"42\"}]}' | from-json | to-bson | from-bson | get root | nth 1 | get z | echo $it" + ); + + assert_eq!(output, "42"); +} + #[test] fn can_convert_table_to_toml_text_and_from_toml_text_back_into_table() { nu!( From 738675259e21f30d869bb033b88fae364b14db4d Mon Sep 17 00:00:00 2001 From: Patrick Meredith Date: Mon, 26 Aug 2019 21:26:49 -0400 Subject: [PATCH 14/15] Improve test so that it should work on Windows --- tests/command_open_tests.rs | 4 ++-- tests/filters_test.rs | 4 ++-- tests/fixtures/formats/sample.bson | Bin 439 -> 521 bytes 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/command_open_tests.rs b/tests/command_open_tests.rs index bf33ec63f1..3f6da5adf5 100644 --- a/tests/command_open_tests.rs +++ b/tests/command_open_tests.rs @@ -28,7 +28,7 @@ fn open_can_parse_bson_1() { nu!( output, cwd("tests/fixtures/formats"), - "open sample.bson | nth 0 | get b | echo $it" + "open sample.bson | get root | nth 0 | get b | echo $it" ); assert_eq!(output, "hello"); @@ -39,7 +39,7 @@ fn open_can_parse_bson_2() { nu!( output, cwd("tests/fixtures/formats"), - "open sample.bson | nth 6 | get b | get '$binary_subtype' | echo $it " + "open sample.bson | get root | nth 6 | get b | get '$binary_subtype' | echo $it " ); assert_eq!(output, "function"); diff --git a/tests/filters_test.rs b/tests/filters_test.rs index ac68ea5fff..710f0b89a8 100644 --- a/tests/filters_test.rs +++ b/tests/filters_test.rs @@ -111,10 +111,10 @@ fn can_convert_json_text_to_bson_and_back_into_table() { nu!( output, cwd("tests/fixtures/formats"), - "echo '{\"root\":[{\"x\": 2, \"y\": 4}, {\"z\": \"42\"}]}' | from-json | to-bson | from-bson | get root | nth 1 | get z | echo $it" + "open sample.bson | to-bson | from-bson | get root | nth 1 | get b | echo $it" ); - assert_eq!(output, "42"); + assert_eq!(output, "whel"); } #[test] diff --git a/tests/fixtures/formats/sample.bson b/tests/fixtures/formats/sample.bson index 95c98eb4e1f1f8ec1517944c23045ba72b098940..951c805065e7d736a2238be45ad4f23ff9734dd5 100644 GIT binary patch delta 227 zcmdna+{vQE$;80G9-o=Q5S!96@gT?9O|5o0Cs~T}^Gg`sGBPkQ8!$}BWMVd)m|@Mt zY&3C^hCj10gBJr#PvW`z#fBBN0&f^?nUfd{f#OWb`6;Ok+(1@NYKcOHf~`WOH3M^T za(+Q7g8)#Ju@XptfPe##U^bb|%BaM|Y&uzuQOTRxjKK{k2ysr5nfJ`N%aejRwFQ|I o8Ki*Xm_{30Gcd3wfo)^VNX^N~S18Xf%1L2hHlN(YXbSWh0B{#FoB#j- delta 144 zcmeBV+0HysDRW|<)x^{4);0_b4D9ilDGafR=k6C9R@4f-VYKB>Vo(N(a|3ZsYKcOH zf~`WOHG=?<%UB5{KtRBOVX`Bm(&RKoMJsC{A7Vz5nfJ`N%aejRwFP+-QS=*IGq5Is WMHn+ub8_+(%JYkIQYK$wGz9 Date: Tue, 27 Aug 2019 13:46:38 +1200 Subject: [PATCH 15/15] Fix having to clean directories when switching between release and debug --- src/cli.rs | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/src/cli.rs b/src/cli.rs index 614ad28f96..9054bfd2d6 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -121,19 +121,24 @@ fn load_plugins(context: &mut Context) -> Result<(), ShellError> { None => println!("PATH is not defined in the environment."), } - // Also use our debug output for now - let mut path = std::path::PathBuf::from("."); - path.push("target"); - path.push("debug"); + #[cfg(debug_assertions)] + { + // Use our debug plugins in debug mode + let mut path = std::path::PathBuf::from("."); + path.push("target"); + path.push("debug"); + let _ = load_plugins_in_dir(&path, context); + } - let _ = load_plugins_in_dir(&path, context); + #[cfg(not(debug_assertions))] + { + // Use our release plugins in release mode + let mut path = std::path::PathBuf::from("."); + path.push("target"); + path.push("release"); - // Also use our release output for now - let mut path = std::path::PathBuf::from("."); - path.push("target"); - path.push("release"); - - let _ = load_plugins_in_dir(&path, context); + let _ = load_plugins_in_dir(&path, context); + } Ok(()) }