From 65a0d27c8a007643b36feededf6a84df0629b7f5 Mon Sep 17 00:00:00 2001 From: Jonathan Turner Date: Thu, 4 Jul 2019 17:11:56 +1200 Subject: [PATCH] Add binary type and tree sink --- Cargo.toml | 4 ++ src/cli.rs | 5 +- src/commands/autoview.rs | 9 ++-- src/commands/enter.rs | 15 ++++-- src/commands/open.rs | 30 ++++++----- src/commands/to_json.rs | 7 +++ src/commands/to_toml.rs | 3 ++ src/env/host.rs | 2 +- src/format/generic.rs | 5 ++ src/format/tree.rs | 8 +-- src/lib.rs | 1 + src/object/base.rs | 14 +++-- src/object/dict.rs | 2 +- src/plugins/treeview.rs | 114 +++++++++++++++++++++++++++++++++++++++ 14 files changed, 181 insertions(+), 38 deletions(-) create mode 100644 src/plugins/treeview.rs diff --git a/Cargo.toml b/Cargo.toml index 38a1f68171..57c430ca7f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -84,6 +84,10 @@ path = "src/plugins/inc.rs" name = "nu_plugin_newskip" path = "src/plugins/newskip.rs" +[[bin]] +name = "nu_plugin_treeview" +path = "src/plugins/treeview.rs" + [[bin]] name = "nu" path = "src/main.rs" diff --git a/src/cli.rs b/src/cli.rs index b6bba49189..4b74979a63 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -34,7 +34,7 @@ pub enum MaybeOwned<'a, T> { } impl MaybeOwned<'a, T> { - crate fn borrow(&self) -> &T { + pub fn borrow(&self) -> &T { match self { MaybeOwned::Owned(v) => v, MaybeOwned::Borrowed(v) => v, @@ -59,6 +59,7 @@ fn load_plugin(path: &std::path::Path, context: &mut Context) -> Result<(), Shel let request = JsonRpc::new("config", Vec::::new()); let request_raw = serde_json::to_string(&request).unwrap(); stdin.write(format!("{}\n", request_raw).as_bytes())?; + let path = dunce::canonicalize(path).unwrap(); let mut input = String::new(); match reader.read_line(&mut input) { @@ -129,14 +130,12 @@ 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"); let _ = load_plugins_in_dir(&path, context); - */ Ok(()) } diff --git a/src/commands/autoview.rs b/src/commands/autoview.rs index c38578324d..6894b0559b 100644 --- a/src/commands/autoview.rs +++ b/src/commands/autoview.rs @@ -5,11 +5,10 @@ use crate::prelude::*; pub fn autoview(args: SinkCommandArgs) -> Result<(), ShellError> { if args.input.len() > 0 { - if equal_shapes(&args.input) { - let mut host = args.ctx.host.lock().unwrap(); - let view = TableView::from_list(&args.input).unwrap(); - - handle_unexpected(&mut *host, |host| crate::format::print_view(&view, host)); + if let Value::Binary(_) = args.input[0] { + println!("Binary"); + } else if equal_shapes(&args.input) { + args.ctx.get_sink("table").run(args)?; } else { let mut host = args.ctx.host.lock().unwrap(); for i in args.input.iter() { diff --git a/src/commands/enter.rs b/src/commands/enter.rs index d699db5d01..d8eba93c9a 100644 --- a/src/commands/enter.rs +++ b/src/commands/enter.rs @@ -67,11 +67,16 @@ pub fn enter(args: CommandArgs) -> Result { } }; - stream.push_back(ReturnValue::Action(CommandAction::Enter(parse_as_value( - file_extension, - contents, - span, - )?))); + match contents { + Value::Primitive(Primitive::String(x)) => { + stream.push_back(ReturnValue::Action(CommandAction::Enter(parse_as_value( + file_extension, + x, + span, + )?))); + } + x => stream.push_back(ReturnValue::Action(CommandAction::Enter(x))), + } Ok(stream.boxed()) } diff --git a/src/commands/open.rs b/src/commands/open.rs index a030863979..39227b0662 100644 --- a/src/commands/open.rs +++ b/src/commands/open.rs @@ -40,7 +40,7 @@ pub fn fetch( cwd: &PathBuf, location: &str, span: Span, -) -> Result<(Option, String), ShellError> { +) -> Result<(Option, Value), ShellError> { let mut cwd = cwd.clone(); if location.starts_with("http:") || location.starts_with("https:") { let response = reqwest::get(location); @@ -71,7 +71,7 @@ pub fn fetch( None => path_extension, }; - Ok((extension, s)) + Ok((extension, Value::string(s))) } Err(_) => { return Err(ShellError::labeled_error( @@ -91,12 +91,15 @@ pub fn fetch( } } else { cwd.push(Path::new(location)); - match std::fs::read_to_string(&cwd) { - Ok(s) => Ok(( - cwd.extension() - .map(|name| name.to_string_lossy().to_string()), - s, - )), + match std::fs::read(&cwd) { + Ok(bytes) => match std::str::from_utf8(&bytes) { + Ok(s) => Ok(( + cwd.extension() + .map(|name| name.to_string_lossy().to_string()), + Value::string(s), + )), + Err(_) => Ok((None, Value::Binary(bytes))), + }, Err(_) => { return Err(ShellError::labeled_error( "File cound not be opened", @@ -227,11 +230,12 @@ fn open(args: CommandArgs) -> Result { } }; - stream.push_back(ReturnValue::Value(parse_as_value( - file_extension, - contents, - span, - )?)); + match contents { + Value::Primitive(Primitive::String(x)) => { + stream.push_back(ReturnValue::Value(parse_as_value(file_extension, x, span)?)); + } + x => stream.push_back(ReturnValue::Value(x)), + } Ok(stream.boxed()) } diff --git a/src/commands/to_json.rs b/src/commands/to_json.rs index 8d78ad9ace..497b0310cb 100644 --- a/src/commands/to_json.rs +++ b/src/commands/to_json.rs @@ -24,6 +24,13 @@ pub fn value_to_json_value(v: &Value) -> serde_json::Value { } Value::Error(e) => serde_json::Value::String(e.to_string()), Value::Block(_) => serde_json::Value::Null, + Value::Binary(b) => serde_json::Value::Array( + b.iter() + .map(|x| { + serde_json::Value::Number(serde_json::Number::from_f64(*x as f64).unwrap()) + }) + .collect(), + ), Value::Object(o) => { let mut m = serde_json::Map::new(); for (k, v) in o.entries.iter() { diff --git a/src/commands/to_toml.rs b/src/commands/to_toml.rs index 256dde52db..2e90b99fa9 100644 --- a/src/commands/to_toml.rs +++ b/src/commands/to_toml.rs @@ -18,6 +18,9 @@ pub fn value_to_toml_value(v: &Value) -> toml::Value { Value::List(l) => toml::Value::Array(l.iter().map(|x| value_to_toml_value(x)).collect()), Value::Error(e) => toml::Value::String(e.to_string()), Value::Block(_) => toml::Value::String("".to_string()), + Value::Binary(b) => { + toml::Value::Array(b.iter().map(|x| toml::Value::Integer(*x as i64)).collect()) + } Value::Object(o) => { let mut m = toml::map::Map::new(); for (k, v) in o.entries.iter() { diff --git a/src/env/host.rs b/src/env/host.rs index 787b728569..8033f86dd2 100644 --- a/src/env/host.rs +++ b/src/env/host.rs @@ -40,7 +40,7 @@ impl Host for Box { } #[derive(Debug)] -crate struct BasicHost; +pub struct BasicHost; impl Host for BasicHost { fn out_terminal(&self) -> Box { diff --git a/src/format/generic.rs b/src/format/generic.rs index ba99123e8c..f5fdf07722 100644 --- a/src/format/generic.rs +++ b/src/format/generic.rs @@ -41,6 +41,11 @@ impl RenderView for GenericView<'value> { Ok(()) } + Value::Binary(_) => { + host.stdout(""); + Ok(()) + } + Value::Filesystem => { host.stdout(""); Ok(()) diff --git a/src/format/tree.rs b/src/format/tree.rs index e9a59e86a5..5492adf15d 100644 --- a/src/format/tree.rs +++ b/src/format/tree.rs @@ -7,14 +7,8 @@ use ptree::print_config::PrintConfig; use ptree::style::{Color, Style}; use ptree::TreeBuilder; -// An entries list is printed like this: -// -// name : ... -// name2 : ... -// another_name : ... #[derive(new)] pub struct TreeView { - //entries: Vec<(crate::object::DescriptorName, Value)>, tree: StringItem, } @@ -39,6 +33,7 @@ impl TreeView { Value::Block(_) => {} Value::Error(_) => {} Value::Filesystem => {} + Value::Binary(_) => {} } } crate fn from_value(value: &Value) -> TreeView { @@ -73,7 +68,6 @@ impl RenderView for TreeView { bold: true, ..Style::default() }; - //config.characters = UTF_CHARS_BOLD.into(); config.indent = 4; config }; diff --git a/src/lib.rs b/src/lib.rs index 49953b3c72..fe30296c8f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -21,6 +21,7 @@ mod shell; mod stream; pub use crate::commands::command::ReturnValue; +pub use crate::env::host::BasicHost; pub use crate::parser::parse::span::SpannedItem; pub use crate::parser::Spanned; pub use crate::plugin::{serve_plugin, Plugin}; diff --git a/src/object/base.rs b/src/object/base.rs index d1dd7cfc4f..9e616049e7 100644 --- a/src/object/base.rs +++ b/src/object/base.rs @@ -75,7 +75,7 @@ impl Primitive { } } - crate fn format(&self, field_name: Option<&String>) -> String { + pub fn format(&self, field_name: Option<&String>) -> String { match self { Primitive::Nothing => format!("{}", Color::Black.bold().paint("-")), Primitive::EndOfStream => format!("{}", Color::Black.bold().paint("-")), @@ -180,6 +180,8 @@ pub enum Value { Primitive(Primitive), Object(crate::object::Dictionary), List(Vec), + Binary(Vec), + #[allow(unused)] Block(Block), Filesystem, @@ -217,6 +219,7 @@ impl fmt::Debug for ValueDebug<'a> { Value::Block(_) => write!(f, "[[block]]"), Value::Error(err) => write!(f, "[[error :: {} ]]", err), Value::Filesystem => write!(f, "[[filesystem]]"), + Value::Binary(_) => write!(f, "[[binary]]"), } } } @@ -237,6 +240,7 @@ impl Value { Value::Block(_) => format!("block"), Value::Error(_) => format!("error"), Value::Filesystem => format!("filesystem"), + Value::Binary(_) => format!("binary"), } } @@ -244,7 +248,7 @@ impl Value { ValueDebug { value: self } } - crate fn data_descriptors(&self) -> Vec { + pub fn data_descriptors(&self) -> Vec { match self { Value::Primitive(_) => vec![], Value::Object(o) => o @@ -257,6 +261,7 @@ impl Value { Value::List(_) => vec![], Value::Error(_) => vec![], Value::Filesystem => vec![], + Value::Binary(_) => vec![], } } @@ -286,7 +291,7 @@ impl Value { } } - crate fn get_data(&'a self, desc: &String) -> MaybeOwned<'a, Value> { + pub fn get_data(&'a self, desc: &String) -> MaybeOwned<'a, Value> { match self { p @ Value::Primitive(_) => MaybeOwned::Borrowed(p), p @ Value::Filesystem => MaybeOwned::Borrowed(p), @@ -294,6 +299,7 @@ impl Value { Value::Block(_) => MaybeOwned::Owned(Value::nothing()), Value::List(_) => MaybeOwned::Owned(Value::nothing()), Value::Error(e) => MaybeOwned::Owned(Value::string(&format!("{:#?}", e))), + Value::Binary(_) => MaybeOwned::Owned(Value::nothing()), } } @@ -308,6 +314,7 @@ impl Value { } Value::Error(e) => Value::Error(Box::new(e.copy_error())), Value::Filesystem => Value::Filesystem, + Value::Binary(b) => Value::Binary(b.clone()), } } @@ -324,6 +331,7 @@ impl Value { Value::List(_) => format!("[list List]"), Value::Error(e) => format!("{}", e), Value::Filesystem => format!(""), + Value::Binary(_) => format!(""), } } diff --git a/src/object/dict.rs b/src/object/dict.rs index 225de4fe92..9c0e4ed34f 100644 --- a/src/object/dict.rs +++ b/src/object/dict.rs @@ -86,7 +86,7 @@ impl Dictionary { out } - crate fn get_data(&'a self, desc: &String) -> MaybeOwned<'a, Value> { + pub fn get_data(&'a self, desc: &String) -> MaybeOwned<'a, Value> { match self.entries.get(desc) { Some(v) => MaybeOwned::Borrowed(v), None => MaybeOwned::Owned(Value::Primitive(Primitive::Nothing)), diff --git a/src/plugins/treeview.rs b/src/plugins/treeview.rs new file mode 100644 index 0000000000..0ff78dc998 --- /dev/null +++ b/src/plugins/treeview.rs @@ -0,0 +1,114 @@ +use derive_new::new; +use indexmap::IndexMap; +use nu::{serve_plugin, Args, CommandConfig, Plugin, ShellError, Value}; +use ptree::item::StringItem; +use ptree::output::print_tree_with; +use ptree::print_config::PrintConfig; +use ptree::style::{Color, Style}; +use ptree::TreeBuilder; + +#[derive(new)] +pub struct TreeView { + tree: StringItem, +} + +impl TreeView { + fn from_value_helper(value: &Value, mut builder: &mut TreeBuilder) { + match value { + Value::Primitive(p) => { + let _ = builder.add_empty_child(p.format(None)); + } + Value::Object(o) => { + for (k, v) in o.entries.iter() { + builder = builder.begin_child(k.clone()); + Self::from_value_helper(v, builder); + builder = builder.end_child(); + } + } + Value::List(l) => { + for elem in l.iter() { + Self::from_value_helper(elem, builder); + } + } + Value::Block(_) => {} + Value::Error(_) => {} + Value::Filesystem => {} + Value::Binary(_) => {} + } + } + + fn from_value(value: &Value) -> TreeView { + let descs = value.data_descriptors(); + + let mut tree = TreeBuilder::new("".to_string()); + let mut builder = &mut tree; + + for desc in descs { + let value = value.get_data(&desc); + builder = builder.begin_child(desc.clone()); + Self::from_value_helper(value.borrow(), &mut builder); + builder = builder.end_child(); + //entries.push((desc.name.clone(), value.borrow().copy())) + } + + TreeView::new(builder.build()) + } + + fn render_view(&self) -> Result<(), ShellError> { + // Set up the print configuration + let config = { + let mut config = PrintConfig::from_env(); + config.branch = Style { + foreground: Some(Color::Green), + dimmed: true, + ..Style::default() + }; + config.leaf = Style { + bold: true, + ..Style::default() + }; + //config.characters = UTF_CHARS_BOLD.into(); + config.indent = 4; + config + }; + + // Print out the tree using custom formatting + print_tree_with(&self.tree, &config)?; + + Ok(()) + } +} + +struct TreeViewer; + +impl Plugin for TreeViewer { + fn config(&mut self) -> Result { + Ok(CommandConfig { + name: "treeview".to_string(), + mandatory_positional: vec![], + optional_positional: vec![], + can_load: vec![], + can_save: vec![], + is_filter: false, + is_sink: true, + named: IndexMap::new(), + rest_positional: true, + }) + } + + fn sink(&mut self, _args: Args, input: Vec) { + if input.len() > 0 { + for i in input.iter() { + let view = TreeView::from_value(&i); + let _ = view.render_view(); + //handle_unexpected(&mut *host, |host| crate::format::print_view(&view, host)); + } + } + + //Ok(()) + } +} + +fn main() { + serve_plugin(&mut TreeViewer); +}