From 31dd579d6faa00b3e0e9b31a1329e32493dd90c3 Mon Sep 17 00:00:00 2001 From: Yehuda Katz Date: Wed, 22 May 2019 21:30:43 -0700 Subject: [PATCH 1/5] Small restructuring --- Cargo.lock | 91 ++++++++++++-- Cargo.toml | 7 +- src/cli.rs | 240 ++++++++++++++++++++++++++++++++++++ src/commands/classified.rs | 4 +- src/commands/command.rs | 3 +- src/context.rs | 8 +- src/errors.rs | 1 - src/format/entries.rs | 1 - src/format/generic.rs | 2 +- src/format/list.rs | 2 +- src/format/table.rs | 2 +- src/main.rs | 241 +------------------------------------ src/object/base.rs | 17 +-- src/object/dict.rs | 2 - src/parser/completer.rs | 3 +- src/prelude.rs | 4 +- src/stream.rs | 0 17 files changed, 354 insertions(+), 274 deletions(-) create mode 100644 src/cli.rs create mode 100644 src/stream.rs diff --git a/Cargo.lock b/Cargo.lock index d7a304c059..25d84b06b5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -651,16 +651,72 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] -name = "futures" -version = "0.1.27" +name = "futures-channel-preview" +version = "0.3.0-alpha.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "futures-core-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "futures-core-preview" +version = "0.3.0-alpha.16" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] -name = "futures-core" -version = "0.2.1" +name = "futures-executor-preview" +version = "0.3.0-alpha.16" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "either 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-channel-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-core-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-util-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)", + "num_cpus 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)", + "pin-utils 0.1.0-alpha.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "futures-io-preview" +version = "0.3.0-alpha.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "futures-core-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "futures-preview" +version = "0.3.0-alpha.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "futures-channel-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-core-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-executor-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-io-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-sink-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-util-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "futures-sink-preview" +version = "0.3.0-alpha.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "futures-channel-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-core-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "futures-util-preview" +version = "0.3.0-alpha.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "futures-channel-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-core-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-io-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-sink-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)", + "memchr 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "pin-utils 0.1.0-alpha.4 (registry+https://github.com/rust-lang/crates.io-index)", + "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -859,12 +915,12 @@ dependencies = [ "cursive 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", "derive-new 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)", "dunce 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-core 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)", "indexmap 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "nom 5.0.0-beta1 (registry+https://github.com/rust-lang/crates.io-index)", "ordered-float 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "pancurses 0.16.1 (registry+https://github.com/rust-lang/crates.io-index)", "prettyprint 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", "prettytable-rs 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "rustyline 4.1.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1031,6 +1087,11 @@ dependencies = [ "libc 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "pin-utils" +version = "0.1.0-alpha.4" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "pkg-config" version = "0.3.14" @@ -1395,6 +1456,11 @@ dependencies = [ "libc 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "slab" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "smallvec" version = "0.6.9" @@ -1775,8 +1841,13 @@ dependencies = [ "checksum flate2 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)" = "f87e68aa82b2de08a6e037f1385455759df6e445a8df5e005b4297191dbf18aa" "checksum fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2fad85553e09a6f881f739c29f0b00b0f01357c743266d478b68951ce23285f3" "checksum fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" -"checksum futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)" = "a2037ec1c6c1c4f79557762eab1f7eae1f64f6cb418ace90fae88f0942b60139" -"checksum futures-core 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7455c91eb2eae38f33b013f77ebe766c75761af333efd9d550e154045c63e225" +"checksum futures-channel-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)" = "4cd523712fc272e9b714669165a2832debee5a5b7e409bfccdc7c0d5cd0cf07a" +"checksum futures-core-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)" = "719770f328642b657b849856bb5a607db9538dd5bb3000122e5ead55d0a58c36" +"checksum futures-executor-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)" = "315dc58c908535d059576a329b86cd185933433382cfcd394fb2fa353330de03" +"checksum futures-io-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)" = "cca0bf7a1f39c9d32b797b0def93d5932aa71796236aad6b549bac6f7df159a3" +"checksum futures-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)" = "fcfeac5f016a4b5835bb93eb7961f50a64f0e001207562703d9ddf4109d7b263" +"checksum futures-sink-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)" = "49dcfdacd6b5974ca0b9b78bc38ffd1071da0206179735c3df82e279f5b784e4" +"checksum futures-util-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)" = "f7a0451b9c5047c2b9ab93425ffd0793165511e93c04b977cd45fbd41c6e34b2" "checksum hashbrown 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "570178d5e4952010d138b0f1d581271ff3a02406d990f887d1e87e3d6e43b0ac" "checksum humantime 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3ca7e5f2e110db35f93b837c81797f3714500b81d517bf20c431b16d3ca4f114" "checksum ident_case 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" @@ -1819,6 +1890,7 @@ dependencies = [ "checksum parking_lot_core 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cb88cb1cb3790baa6776844f968fea3be44956cf184fa1be5a03341f5491278c" "checksum parse-zoneinfo 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "089a398ccdcdd77b8c38909d5a1e4b67da1bc4c9dbfe6d5b536c828eddb779e5" "checksum pdcurses-sys 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "084dd22796ff60f1225d4eb6329f33afaf4c85419d51d440ab6b8c6f4529166b" +"checksum pin-utils 0.1.0-alpha.4 (registry+https://github.com/rust-lang/crates.io-index)" = "5894c618ce612a3fa23881b152b608bafb8c56cfc22f434a3ba3120b40f7b587" "checksum pkg-config 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)" = "676e8eb2b1b4c9043511a9b7bea0915320d7e502b0a079fb03f9635a5252b18c" "checksum plist 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f4739851c08dd9a62a78beff2edf1a438517268b2c563c42fc6d9d3139e42d2a" "checksum prettyprint 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2705417f8aa07cb6308db42e55623479c1c9667942a4d5e4174c684e5da5590d" @@ -1861,6 +1933,7 @@ dependencies = [ "checksum shell-words 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "39acde55a154c4cd3ae048ac78cc21c25f3a0145e44111b523279113dce0d94a" "checksum signal-hook 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "72ab58f1fda436857e6337dcb6a5aaa34f16c5ddc87b3a8b6ef7a212f90b9c5a" "checksum signal-hook-registry 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cded4ffa32146722ec54ab1f16320568465aa922aa9ab4708129599740da85d7" +"checksum slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" "checksum smallvec 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)" = "c4488ae950c49d403731982257768f48fada354a5203fe81f9bb6f43ca9002be" "checksum stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dba1a27d3efae4351c8051072d619e3ade2820635c3958d826bfea39d59b54c8" "checksum stackvector 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c049c77bf85fbc036484c97b008276d539d9ebff9dfbde37b632ebcd5b8746b6" diff --git a/Cargo.toml b/Cargo.toml index be014c70dc..02ae4a3a60 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,11 +21,14 @@ conch-parser = "0.1.1" nom = "5.0.0-beta1" subprocess = "0.1.18" dunce = "1.0.0" -futures = "0.1.27" -futures-core = "0.2.1" indexmap = "1.0.2" chrono-humanize = "0.0.11" byte-unit = "2.1.0" ordered-float = "1.0.2" prettyprint = "0.6.0" cursive = { version = "0.12.0", features = ["pancurses-backend"], default-features = false } +futures-preview = "0.3.0-alpha.16" + +[dependencies.pancurses] +version = "0.16" +features = ["win32a"] \ No newline at end of file diff --git a/src/cli.rs b/src/cli.rs new file mode 100644 index 0000000000..bc16001036 --- /dev/null +++ b/src/cli.rs @@ -0,0 +1,240 @@ +use crate::prelude::*; + +use crate::commands::classified::{ClassifiedCommand, ExternalCommand, InternalCommand}; +use crate::commands::command::ReturnValue; +use crate::context::Context; +crate use crate::env::Host; +crate use crate::errors::ShellError; +crate use crate::format::{EntriesListView, GenericView}; +use crate::object::Value; + +use rustyline::error::ReadlineError; +use rustyline::{self, ColorMode, Config, Editor}; +use std::collections::VecDeque; +use std::error::Error; +use std::sync::Arc; + +#[derive(Debug)] +pub enum MaybeOwned<'a, T> { + Owned(T), + Borrowed(&'a T), +} + +impl MaybeOwned<'a, T> { + crate fn borrow(&self) -> &T { + match self { + MaybeOwned::Owned(v) => v, + MaybeOwned::Borrowed(v) => v, + } + } +} + +pub fn cli() -> Result<(), Box> { + let config = Config::builder().color_mode(ColorMode::Forced).build(); + let h = crate::shell::Helper::new(); + let mut rl: Editor = Editor::with_config(config); + + #[cfg(windows)] + { + let _ = ansi_term::enable_ansi_support(); + } + + rl.set_helper(Some(h)); + if rl.load_history("history.txt").is_err() { + println!("No previous history."); + } + + let mut context = Context::basic()?; + + { + use crate::commands::*; + + context.add_commands(vec![ + ("format", Arc::new(format)), + ("format-list", Arc::new(format_list)), + ("ps", Arc::new(ps::ps)), + ("ls", Arc::new(ls::ls)), + ("cd", Arc::new(cd::cd)), + ("view", Arc::new(view::view)), + ("skip", Arc::new(skip::skip)), + ("take", Arc::new(take::take)), + ("select", Arc::new(select::select)), + ("reject", Arc::new(reject::reject)), + ("to-array", Arc::new(to_array::to_array)), + ("where", Arc::new(where_::r#where)), + ("sort-by", Arc::new(sort_by::sort_by)), + ]); + } + + loop { + let readline = rl.readline(&format!("{}> ", context.env.cwd().display().to_string())); + + match process_line(readline, &mut context) { + LineResult::Success(line) => { + rl.add_history_entry(line.clone()); + } + + LineResult::Error(err) => { + context.host.stdout(&err); + } + + LineResult::Break => { + break; + } + + LineResult::FatalError(err) => { + context + .host + .stdout(&format!("A surprising fatal error occurred.\n{:?}", err)); + } + } + } + rl.save_history("history.txt").unwrap(); + + Ok(()) +} + +enum LineResult { + Success(String), + Error(String), + Break, + + #[allow(unused)] + FatalError(ShellError), +} + +fn process_line(readline: Result, ctx: &mut Context) -> LineResult { + match &readline { + Ok(line) if line.trim() == "exit" => LineResult::Break, + + Ok(line) if line.trim() == "" => LineResult::Success(line.clone()), + + Ok(line) => { + let result = match crate::parser::shell_parser(&line) { + Err(err) => { + return LineResult::Error(format!("{:?}", err)); + } + + Ok(val) => val, + }; + + let parsed = result.1; + + let mut input = VecDeque::new(); + + for item in parsed { + input = match process_command(item.clone(), input, ctx) { + Ok(val) => val, + Err(err) => return LineResult::Error(format!("{}", err.description())), + }; + } + + if input.len() > 0 { + if equal_shapes(&input) { + let array = crate::commands::stream_to_array(input); + let args = CommandArgs::from_context(ctx, vec![], array); + match format(args) { + Ok(_) => {} + Err(err) => return LineResult::Error(err.to_string()), + } + } else { + let args = CommandArgs::from_context(ctx, vec![], input); + match format(args) { + Ok(_) => {} + Err(err) => return LineResult::Error(err.to_string()), + } + } + } + + LineResult::Success(line.to_string()) + } + Err(ReadlineError::Interrupted) => { + println!("CTRL-C"); + LineResult::Break + } + Err(ReadlineError::Eof) => { + println!("CTRL-D"); + LineResult::Break + } + Err(err) => { + println!("Error: {:?}", err); + LineResult::Break + } + } +} + +fn process_command( + parsed: Vec, + input: VecDeque, + context: &mut Context, +) -> Result, ShellError> { + let command = classify_command(&parsed, context)?; + + command.run(input, context) +} + +fn classify_command( + command: &[crate::parser::Item], + context: &Context, +) -> Result { + let command_name = &command[0].name()?; + + let arg_list: Vec = command[1..].iter().map(|i| i.as_value()).collect(); + let arg_list_strings: Vec = command[1..].iter().map(|i| i.print()).collect(); + + match *command_name { + other => match context.has_command(*command_name) { + true => { + let command = context.get_command(command_name); + Ok(ClassifiedCommand::Internal(InternalCommand { + command, + args: arg_list, + })) + } + false => Ok(ClassifiedCommand::External(ExternalCommand { + name: other.to_string(), + args: arg_list_strings, + })), + }, + } +} + +fn format(args: CommandArgs<'caller>) -> Result, ShellError> { + let last = args.input.len() - 1; + for (i, item) in args.input.iter().enumerate() { + let view = GenericView::new(item); + crate::format::print_view(&view, args.host); + + if last != i { + println!(""); + } + } + + Ok(VecDeque::new()) +} + +fn format_list(args: CommandArgs<'caller>) -> Result, ShellError> { + let view = EntriesListView::from_stream(args.input); + crate::format::print_view(&view, args.host); + + Ok(VecDeque::new()) +} + +fn equal_shapes(input: &VecDeque) -> bool { + let mut items = input.iter(); + + let item = match items.next() { + Some(item) => item, + None => return false, + }; + + let desc = item.data_descriptors(); + + for item in items { + if desc != item.data_descriptors() { + return false; + } + } + + true +} diff --git a/src/commands/classified.rs b/src/commands/classified.rs index 07cda3c179..36a23ce6dd 100644 --- a/src/commands/classified.rs +++ b/src/commands/classified.rs @@ -11,7 +11,7 @@ impl ClassifiedCommand { crate fn run( self, input: VecDeque, - context: &mut crate::Context, + context: &mut Context, ) -> Result, ShellError> { match self { ClassifiedCommand::Internal(internal) => { @@ -22,7 +22,7 @@ impl ClassifiedCommand { for v in result { match v { ReturnValue::Action(action) => match action { - crate::CommandAction::ChangeCwd(cwd) => context.env.cwd = cwd, + CommandAction::ChangeCwd(cwd) => context.env.cwd = cwd, }, ReturnValue::Value(v) => next.push_back(v), diff --git a/src/commands/command.rs b/src/commands/command.rs index bba76df199..d48adab03e 100644 --- a/src/commands/command.rs +++ b/src/commands/command.rs @@ -1,12 +1,11 @@ use crate::errors::ShellError; use crate::object::Value; use crate::prelude::*; -use crate::Context; use std::path::PathBuf; pub struct CommandArgs<'caller> { pub host: &'caller mut dyn Host, - pub env: &'caller crate::Environment, + pub env: &'caller Environment, pub args: Vec, pub input: VecDeque, } diff --git a/src/context.rs b/src/context.rs index c60b97b2f8..bcbb30afcc 100644 --- a/src/context.rs +++ b/src/context.rs @@ -4,8 +4,8 @@ use std::error::Error; use std::sync::Arc; pub struct Context { - commands: indexmap::IndexMap>, - crate host: Box, + commands: indexmap::IndexMap>, + crate host: Box, crate env: Environment, } @@ -14,11 +14,11 @@ impl Context { Ok(Context { commands: indexmap::IndexMap::new(), host: Box::new(crate::env::host::BasicHost), - env: crate::Environment::basic()?, + env: Environment::basic()?, }) } - pub fn add_commands(&mut self, commands: Vec<(&str, Arc)>) { + pub fn add_commands(&mut self, commands: Vec<(&str, Arc)>) { for (name, command) in commands { self.commands.insert(name.to_string(), command); } diff --git a/src/errors.rs b/src/errors.rs index 9d9cadc729..eb270f30e1 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -1,7 +1,6 @@ #[allow(unused)] use crate::prelude::*; -use crate::Value; use derive_new::new; #[derive(Debug, Ord, PartialOrd, Eq, PartialEq, new)] diff --git a/src/format/entries.rs b/src/format/entries.rs index 5b7c87e83d..02845c0854 100644 --- a/src/format/entries.rs +++ b/src/format/entries.rs @@ -1,6 +1,5 @@ use crate::format::RenderView; use crate::prelude::*; -use crate::Host; use derive_new::new; diff --git a/src/format/generic.rs b/src/format/generic.rs index 7ba3b132dd..e7bb198a45 100644 --- a/src/format/generic.rs +++ b/src/format/generic.rs @@ -1,6 +1,6 @@ use crate::format::{EntriesView, RenderView, TableView}; use crate::object::Value; -use crate::Host; +use crate::prelude::*; use derive_new::new; // A list is printed one line at a time with an optional separator between groups diff --git a/src/format/list.rs b/src/format/list.rs index ad423c0ad7..508dc7c804 100644 --- a/src/format/list.rs +++ b/src/format/list.rs @@ -1,5 +1,5 @@ use crate::format::RenderView; -use crate::Host; +use crate::prelude::*; use derive_new::new; // A list is printed one line at a time with an optional separator between groups diff --git a/src/format/table.rs b/src/format/table.rs index 02d463a293..8014bf3c7a 100644 --- a/src/format/table.rs +++ b/src/format/table.rs @@ -1,6 +1,6 @@ use crate::format::RenderView; use crate::object::Value; -use crate::Host; +use crate::prelude::*; use derive_new::new; use prettytable::{Cell, Row, Table}; diff --git a/src/main.rs b/src/main.rs index 1422ab4515..6cf266b286 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,9 +1,7 @@ #![feature(crate_visibility_modifier)] #![feature(in_band_lifetimes)] -#[allow(unused)] -use crate::prelude::*; - +mod cli; mod commands; mod context; mod env; @@ -13,243 +11,10 @@ mod object; mod parser; mod prelude; mod shell; +mod stream; -use crate::commands::classified::{ClassifiedCommand, ExternalCommand, InternalCommand}; -use crate::commands::command::ReturnValue; -crate use crate::commands::command::{Command, CommandAction}; -use crate::context::Context; -crate use crate::env::{Environment, Host}; -crate use crate::errors::ShellError; -crate use crate::format::{EntriesListView, GenericView}; -use crate::object::Value; - -use rustyline::error::ReadlineError; -use rustyline::{self, ColorMode, Config, Editor}; -use std::collections::VecDeque; use std::error::Error; -use std::sync::Arc; - -#[derive(Debug)] -pub enum MaybeOwned<'a, T> { - Owned(T), - Borrowed(&'a T), -} - -impl MaybeOwned<'a, T> { - crate fn borrow(&self) -> &T { - match self { - MaybeOwned::Owned(v) => v, - MaybeOwned::Borrowed(v) => v, - } - } -} fn main() -> Result<(), Box> { - let config = Config::builder().color_mode(ColorMode::Forced).build(); - let h = crate::shell::Helper::new(); - let mut rl: Editor = Editor::with_config(config); - - #[cfg(windows)] - { - let _ = ansi_term::enable_ansi_support(); - } - - rl.set_helper(Some(h)); - if rl.load_history("history.txt").is_err() { - println!("No previous history."); - } - - let mut context = Context::basic()?; - - { - use crate::commands::*; - - context.add_commands(vec![ - ("format", Arc::new(format)), - ("format-list", Arc::new(format_list)), - ("ps", Arc::new(ps::ps)), - ("ls", Arc::new(ls::ls)), - ("cd", Arc::new(cd::cd)), - ("view", Arc::new(view::view)), - ("skip", Arc::new(skip::skip)), - ("take", Arc::new(take::take)), - ("select", Arc::new(select::select)), - ("reject", Arc::new(reject::reject)), - ("to-array", Arc::new(to_array::to_array)), - ("where", Arc::new(where_::r#where)), - ("sort-by", Arc::new(sort_by::sort_by)), - ]); - } - - loop { - let readline = rl.readline(&format!("{}> ", context.env.cwd().display().to_string())); - - match process_line(readline, &mut context) { - LineResult::Success(line) => { - rl.add_history_entry(line.clone()); - } - - LineResult::Error(err) => { - context.host.stdout(&err); - } - - LineResult::Break => { - break; - } - - LineResult::FatalError(err) => { - context - .host - .stdout(&format!("A surprising fatal error occurred.\n{:?}", err)); - } - } - } - rl.save_history("history.txt").unwrap(); - - Ok(()) -} - -enum LineResult { - Success(String), - Error(String), - Break, - - #[allow(unused)] - FatalError(ShellError), -} - -fn process_line(readline: Result, ctx: &mut Context) -> LineResult { - match &readline { - Ok(line) if line.trim() == "exit" => LineResult::Break, - - Ok(line) if line.trim() == "" => LineResult::Success(line.clone()), - - Ok(line) => { - let result = match crate::parser::shell_parser(&line) { - Err(err) => { - return LineResult::Error(format!("{:?}", err)); - } - - Ok(val) => val, - }; - - let parsed = result.1; - - let mut input = VecDeque::new(); - - for item in parsed { - input = match process_command(item.clone(), input, ctx) { - Ok(val) => val, - Err(err) => return LineResult::Error(format!("{}", err.description())), - }; - } - - if input.len() > 0 { - if equal_shapes(&input) { - let array = crate::commands::stream_to_array(input); - let args = CommandArgs::from_context(ctx, vec![], array); - match format(args) { - Ok(_) => {} - Err(err) => return LineResult::Error(err.to_string()), - } - } else { - let args = CommandArgs::from_context(ctx, vec![], input); - match format(args) { - Ok(_) => {} - Err(err) => return LineResult::Error(err.to_string()), - } - } - } - - LineResult::Success(line.to_string()) - } - Err(ReadlineError::Interrupted) => { - println!("CTRL-C"); - LineResult::Break - } - Err(ReadlineError::Eof) => { - println!("CTRL-D"); - LineResult::Break - } - Err(err) => { - println!("Error: {:?}", err); - LineResult::Break - } - } -} - -fn process_command( - parsed: Vec, - input: VecDeque, - context: &mut Context, -) -> Result, ShellError> { - let command = classify_command(&parsed, context)?; - - command.run(input, context) -} - -fn classify_command( - command: &[crate::parser::Item], - context: &Context, -) -> Result { - let command_name = &command[0].name()?; - - let arg_list: Vec = command[1..].iter().map(|i| i.as_value()).collect(); - let arg_list_strings: Vec = command[1..].iter().map(|i| i.print()).collect(); - - match *command_name { - other => match context.has_command(*command_name) { - true => { - let command = context.get_command(command_name); - Ok(ClassifiedCommand::Internal(InternalCommand { - command, - args: arg_list, - })) - } - false => Ok(ClassifiedCommand::External(ExternalCommand { - name: other.to_string(), - args: arg_list_strings, - })), - }, - } -} - -fn format(args: CommandArgs<'caller>) -> Result, ShellError> { - let last = args.input.len() - 1; - for (i, item) in args.input.iter().enumerate() { - let view = GenericView::new(item); - crate::format::print_view(&view, args.host); - - if last != i { - println!(""); - } - } - - Ok(VecDeque::new()) -} - -fn format_list(args: CommandArgs<'caller>) -> Result, ShellError> { - let view = EntriesListView::from_stream(args.input); - crate::format::print_view(&view, args.host); - - Ok(VecDeque::new()) -} - -fn equal_shapes(input: &VecDeque) -> bool { - let mut items = input.iter(); - - let item = match items.next() { - Some(item) => item, - None => return false, - }; - - let desc = item.data_descriptors(); - - for item in items { - if desc != item.data_descriptors() { - return false; - } - } - - true + crate::cli::cli() } diff --git a/src/object/base.rs b/src/object/base.rs index dae2d2e4e1..b3cc291f2a 100644 --- a/src/object/base.rs +++ b/src/object/base.rs @@ -1,6 +1,7 @@ use crate::errors::ShellError; use crate::object::desc::DataDescriptor; use crate::parser::parse::Operator; +use crate::prelude::*; use ansi_term::Color; use chrono::{DateTime, Utc}; use chrono_humanize::Humanize; @@ -75,21 +76,21 @@ impl Value { } } - crate fn get_data_by_key(&'a self, name: &str) -> crate::MaybeOwned<'a, Value> { + crate fn get_data_by_key(&'a self, name: &str) -> MaybeOwned<'a, Value> { match self { - Value::Primitive(_) => crate::MaybeOwned::Owned(Value::nothing()), + Value::Primitive(_) => MaybeOwned::Owned(Value::nothing()), Value::Object(o) => o.get_data_by_key(name), - Value::List(_) => crate::MaybeOwned::Owned(Value::nothing()), - Value::Error(_) => crate::MaybeOwned::Owned(Value::nothing()), + Value::List(_) => MaybeOwned::Owned(Value::nothing()), + Value::Error(_) => MaybeOwned::Owned(Value::nothing()), } } - crate fn get_data(&'a self, desc: &DataDescriptor) -> crate::MaybeOwned<'a, Value> { + crate fn get_data(&'a self, desc: &DataDescriptor) -> MaybeOwned<'a, Value> { match self { - Value::Primitive(_) => crate::MaybeOwned::Owned(Value::nothing()), + Value::Primitive(_) => MaybeOwned::Owned(Value::nothing()), Value::Object(o) => o.get_data(desc), - Value::List(_) => crate::MaybeOwned::Owned(Value::nothing()), - Value::Error(_) => crate::MaybeOwned::Owned(Value::nothing()), + Value::List(_) => MaybeOwned::Owned(Value::nothing()), + Value::Error(_) => MaybeOwned::Owned(Value::nothing()), } } diff --git a/src/object/dict.rs b/src/object/dict.rs index 3d561a0a19..52f981701b 100644 --- a/src/object/dict.rs +++ b/src/object/dict.rs @@ -1,9 +1,7 @@ -#[allow(unused)] use crate::prelude::*; use crate::object::desc::DataDescriptor; use crate::object::{Primitive, Value}; -use crate::MaybeOwned; use indexmap::IndexMap; use std::cmp::{Ordering, PartialOrd}; diff --git a/src/parser/completer.rs b/src/parser/completer.rs index 98328e96c1..a22ac3abaf 100644 --- a/src/parser/completer.rs +++ b/src/parser/completer.rs @@ -1,9 +1,10 @@ +use crate::prelude::*; use rustyline::{completion, Context}; use std::collections::BTreeMap; #[allow(unused)] crate struct Completer { - commands: BTreeMap>, + commands: BTreeMap>, } impl completion::Completer for Completer { diff --git a/src/prelude.rs b/src/prelude.rs index a0eb2b3d21..0480c26932 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -1,4 +1,6 @@ -crate use crate::commands::command::{Command, CommandArgs, ReturnValue}; +crate use crate::cli::MaybeOwned; +crate use crate::commands::command::{Command, CommandAction, CommandArgs, ReturnValue}; +crate use crate::context::Context; crate use crate::env::{Environment, Host}; crate use crate::errors::ShellError; crate use crate::object::{Primitive, Value}; diff --git a/src/stream.rs b/src/stream.rs new file mode 100644 index 0000000000..e69de29bb2 From 625a35636176baeb5c98dfcda6feaf2ec6d81dc5 Mon Sep 17 00:00:00 2001 From: Yehuda Katz Date: Thu, 23 May 2019 00:23:06 -0700 Subject: [PATCH 2/5] Data flows across commands via streams now --- rustfmt.toml | 1 + src/cli.rs | 71 +++++++++++++++++++------------------- src/commands/cd.rs | 6 ++-- src/commands/classified.rs | 33 +++++++++--------- src/commands/command.rs | 24 ++++++------- src/commands/ls.rs | 6 ++-- src/commands/ps.rs | 4 +-- src/commands/reject.rs | 12 +++---- src/commands/select.rs | 11 +++--- src/commands/skip.rs | 18 +++------- src/commands/sort_by.rs | 29 +++++++++------- src/commands/take.rs | 18 +++------- src/commands/to_array.rs | 16 +++++---- src/commands/view.rs | 6 ++-- src/commands/where_.rs | 23 ++++++------ src/context.rs | 16 ++++----- src/format/entries.rs | 6 ++-- src/main.rs | 4 ++- src/parser/parse.rs | 2 +- src/prelude.rs | 6 ++++ src/stream.rs | 16 +++++++++ 21 files changed, 171 insertions(+), 157 deletions(-) create mode 100644 rustfmt.toml diff --git a/rustfmt.toml b/rustfmt.toml new file mode 100644 index 0000000000..c51666e8f4 --- /dev/null +++ b/rustfmt.toml @@ -0,0 +1 @@ +edition = "2018" \ No newline at end of file diff --git a/src/cli.rs b/src/cli.rs index bc16001036..afa320e3c8 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -1,12 +1,11 @@ use crate::prelude::*; use crate::commands::classified::{ClassifiedCommand, ExternalCommand, InternalCommand}; -use crate::commands::command::ReturnValue; use crate::context::Context; -crate use crate::env::Host; crate use crate::errors::ShellError; crate use crate::format::{EntriesListView, GenericView}; use crate::object::Value; +use crate::stream::empty_stream; use rustyline::error::ReadlineError; use rustyline::{self, ColorMode, Config, Editor}; @@ -29,7 +28,7 @@ impl MaybeOwned<'a, T> { } } -pub fn cli() -> Result<(), Box> { +pub async fn cli() -> Result<(), Box> { let config = Config::builder().color_mode(ColorMode::Forced).build(); let h = crate::shell::Helper::new(); let mut rl: Editor = Editor::with_config(config); @@ -50,8 +49,6 @@ pub fn cli() -> Result<(), Box> { use crate::commands::*; context.add_commands(vec![ - ("format", Arc::new(format)), - ("format-list", Arc::new(format_list)), ("ps", Arc::new(ps::ps)), ("ls", Arc::new(ls::ls)), ("cd", Arc::new(cd::cd)), @@ -67,15 +64,18 @@ pub fn cli() -> Result<(), Box> { } loop { - let readline = rl.readline(&format!("{}> ", context.env.cwd().display().to_string())); + let readline = rl.readline(&format!( + "{}> ", + context.env.lock().unwrap().cwd().display().to_string() + )); - match process_line(readline, &mut context) { + match process_line(readline, &mut context).await { LineResult::Success(line) => { rl.add_history_entry(line.clone()); } LineResult::Error(err) => { - context.host.stdout(&err); + context.host.lock().unwrap().stdout(&err); } LineResult::Break => { @@ -85,6 +85,8 @@ pub fn cli() -> Result<(), Box> { LineResult::FatalError(err) => { context .host + .lock() + .unwrap() .stdout(&format!("A surprising fatal error occurred.\n{:?}", err)); } } @@ -103,7 +105,7 @@ enum LineResult { FatalError(ShellError), } -fn process_line(readline: Result, ctx: &mut Context) -> LineResult { +async fn process_line(readline: Result, ctx: &mut Context) -> LineResult { match &readline { Ok(line) if line.trim() == "exit" => LineResult::Break, @@ -120,29 +122,25 @@ fn process_line(readline: Result, ctx: &mut Context) -> L let parsed = result.1; - let mut input = VecDeque::new(); + let mut input: InputStream = VecDeque::new().boxed(); for item in parsed { - input = match process_command(item.clone(), input, ctx) { + input = match process_command(item.clone(), input, ctx).await { Ok(val) => val, Err(err) => return LineResult::Error(format!("{}", err.description())), }; } - if input.len() > 0 { - if equal_shapes(&input) { - let array = crate::commands::stream_to_array(input); + let input_vec: VecDeque<_> = input.collect().await; + + if input_vec.len() > 0 { + if equal_shapes(&input_vec) { + let array = crate::commands::stream_to_array(input_vec.boxed()).await; let args = CommandArgs::from_context(ctx, vec![], array); - match format(args) { - Ok(_) => {} - Err(err) => return LineResult::Error(err.to_string()), - } + format(args).await; } else { - let args = CommandArgs::from_context(ctx, vec![], input); - match format(args) { - Ok(_) => {} - Err(err) => return LineResult::Error(err.to_string()), - } + let args = CommandArgs::from_context(ctx, vec![], input_vec.boxed()); + format(args).await; } } @@ -163,14 +161,14 @@ fn process_line(readline: Result, ctx: &mut Context) -> L } } -fn process_command( +async fn process_command( parsed: Vec, - input: VecDeque, + input: InputStream, context: &mut Context, -) -> Result, ShellError> { +) -> Result { let command = classify_command(&parsed, context)?; - command.run(input, context) + command.run(input, context).await } fn classify_command( @@ -199,25 +197,26 @@ fn classify_command( } } -fn format(args: CommandArgs<'caller>) -> Result, ShellError> { - let last = args.input.len() - 1; - for (i, item) in args.input.iter().enumerate() { +async fn format(args: CommandArgs) -> OutputStream { + let input: Vec<_> = args.input.collect().await; + let last = input.len() - 1; + for (i, item) in input.iter().enumerate() { let view = GenericView::new(item); - crate::format::print_view(&view, args.host); + crate::format::print_view(&view, &mut *args.host.lock().unwrap()); if last != i { println!(""); } } - Ok(VecDeque::new()) + empty_stream() } -fn format_list(args: CommandArgs<'caller>) -> Result, ShellError> { - let view = EntriesListView::from_stream(args.input); - crate::format::print_view(&view, args.host); +async fn format_list(args: CommandArgs) -> OutputStream { + let view = EntriesListView::from_stream(args.input).await; + crate::format::print_view(&view, &mut *args.host.lock().unwrap()); - Ok(VecDeque::new()) + empty_stream() } fn equal_shapes(input: &VecDeque) -> bool { diff --git a/src/commands/cd.rs b/src/commands/cd.rs index a4f6523427..ab2f59b314 100644 --- a/src/commands/cd.rs +++ b/src/commands/cd.rs @@ -2,18 +2,18 @@ use crate::errors::ShellError; use crate::prelude::*; use std::env; -pub fn cd(args: CommandArgs<'caller>) -> Result, ShellError> { +pub fn cd(args: CommandArgs) -> Result { let target = match args.args.first() { // TODO: This needs better infra None => return Err(ShellError::string(format!("cd must take one arg"))), Some(v) => v.as_string()?.clone(), }; - let cwd = args.env.cwd().to_path_buf(); + let cwd = args.env.lock().unwrap().cwd().to_path_buf(); let mut stream = VecDeque::new(); let path = dunce::canonicalize(cwd.join(&target).as_path())?; let _ = env::set_current_dir(&path); stream.push_back(ReturnValue::change_cwd(path)); - Ok(stream) + Ok(stream.boxed()) } diff --git a/src/commands/classified.rs b/src/commands/classified.rs index 36a23ce6dd..a528b9bd34 100644 --- a/src/commands/classified.rs +++ b/src/commands/classified.rs @@ -8,37 +8,38 @@ crate enum ClassifiedCommand { } impl ClassifiedCommand { - crate fn run( + crate async fn run( self, - input: VecDeque, + input: InputStream, context: &mut Context, - ) -> Result, ShellError> { + ) -> Result { match self { ClassifiedCommand::Internal(internal) => { let result = context.run_command(internal.command, internal.args, input)?; + let env = context.env.clone(); - let mut next = VecDeque::new(); + let stream = result.filter_map(move |v| match v { + ReturnValue::Action(action) => match action { + CommandAction::ChangeCwd(cwd) => { + env.lock().unwrap().cwd = cwd; + futures::future::ready(None) + } + }, - for v in result { - match v { - ReturnValue::Action(action) => match action { - CommandAction::ChangeCwd(cwd) => context.env.cwd = cwd, - }, + ReturnValue::Value(v) => futures::future::ready(Some(v)), + }); - ReturnValue::Value(v) => next.push_back(v), - } - } - - Ok(next) + Ok(stream.boxed() as InputStream) } ClassifiedCommand::External(external) => { Exec::shell(&external.name) .args(&external.args) - .cwd(context.env.cwd()) + .cwd(context.env.lock().unwrap().cwd()) .join() .unwrap(); - Ok(VecDeque::new()) + + Ok(VecDeque::new().boxed() as InputStream) } } } diff --git a/src/commands/command.rs b/src/commands/command.rs index d48adab03e..71479ceb0f 100644 --- a/src/commands/command.rs +++ b/src/commands/command.rs @@ -3,22 +3,22 @@ use crate::object::Value; use crate::prelude::*; use std::path::PathBuf; -pub struct CommandArgs<'caller> { - pub host: &'caller mut dyn Host, - pub env: &'caller Environment, +pub struct CommandArgs { + pub host: Arc>, + pub env: Arc>, pub args: Vec, - pub input: VecDeque, + pub input: InputStream, } -impl CommandArgs<'caller> { +impl CommandArgs { crate fn from_context( ctx: &'caller mut Context, args: Vec, - input: VecDeque, - ) -> CommandArgs<'caller> { + input: InputStream, + ) -> CommandArgs { CommandArgs { - host: &mut ctx.host, - env: &ctx.env, + host: ctx.host.clone(), + env: ctx.env.clone(), args, input, } @@ -49,14 +49,14 @@ impl ReturnValue { } pub trait Command { - fn run(&self, args: CommandArgs<'caller>) -> Result, ShellError>; + fn run(&self, args: CommandArgs) -> Result; } impl Command for F where - F: Fn(CommandArgs<'_>) -> Result, ShellError>, + F: Fn(CommandArgs) -> Result, { - fn run(&self, args: CommandArgs<'caller>) -> Result, ShellError> { + fn run(&self, args: CommandArgs) -> Result { self(args) } } diff --git a/src/commands/ls.rs b/src/commands/ls.rs index b591bdc02b..8b07c90af4 100644 --- a/src/commands/ls.rs +++ b/src/commands/ls.rs @@ -2,8 +2,8 @@ use crate::errors::ShellError; use crate::object::{dir_entry_dict, Value}; use crate::prelude::*; -pub fn ls(args: CommandArgs<'value>) -> Result, ShellError> { - let cwd = args.env.cwd().to_path_buf(); +pub fn ls(args: CommandArgs) -> Result { + let cwd = args.env.lock().unwrap().cwd().to_path_buf(); let entries = std::fs::read_dir(&cwd).map_err(|e| ShellError::string(format!("{:?}", e)))?; @@ -14,5 +14,5 @@ pub fn ls(args: CommandArgs<'value>) -> Result, ShellError shell_entries.push_back(ReturnValue::Value(value)) } - Ok(shell_entries) + Ok(shell_entries.boxed()) } diff --git a/src/commands/ps.rs b/src/commands/ps.rs index 8e34dab582..dcd61b9b2e 100644 --- a/src/commands/ps.rs +++ b/src/commands/ps.rs @@ -4,7 +4,7 @@ use crate::object::Value; use crate::prelude::*; use sysinfo::SystemExt; -pub fn ps(_args: CommandArgs<'caller>) -> Result, ShellError> { +pub fn ps(_args: CommandArgs) -> Result { let mut system = sysinfo::System::new(); system.refresh_all(); @@ -15,5 +15,5 @@ pub fn ps(_args: CommandArgs<'caller>) -> Result, ShellErr .map(|(_, process)| ReturnValue::Value(Value::Object(process_dict(process)))) .collect::>(); - Ok(list) + Ok(list.boxed()) } diff --git a/src/commands/reject.rs b/src/commands/reject.rs index c7ab2b0142..ef8313ff7b 100644 --- a/src/commands/reject.rs +++ b/src/commands/reject.rs @@ -3,7 +3,7 @@ use crate::object::base::reject_fields; use crate::object::Value; use crate::prelude::*; -pub fn reject(args: CommandArgs<'value>) -> Result, ShellError> { +pub fn reject(args: CommandArgs) -> Result { if args.args.is_empty() { return Err(ShellError::string("select requires a field")); } @@ -11,12 +11,10 @@ pub fn reject(args: CommandArgs<'value>) -> Result, ShellE let fields: Result, _> = args.args.iter().map(|a| a.as_string()).collect(); let fields = fields?; - let objects = args + let stream = args .input - .iter() - .map(|item| Value::Object(reject_fields(item, &fields))) - .map(|item| ReturnValue::Value(item)) - .collect(); + .map(move |item| Value::Object(reject_fields(&item, &fields))) + .map(|item| ReturnValue::Value(item)); - Ok(objects) + Ok(stream.boxed()) } diff --git a/src/commands/select.rs b/src/commands/select.rs index 6e346ed567..3703c6217a 100644 --- a/src/commands/select.rs +++ b/src/commands/select.rs @@ -3,7 +3,7 @@ use crate::object::base::select_fields; use crate::object::Value; use crate::prelude::*; -pub fn select(args: CommandArgs<'caller>) -> Result, ShellError> { +pub fn select(args: CommandArgs) -> Result { if args.args.is_empty() { return Err(ShellError::string("select requires a field")); } @@ -13,10 +13,9 @@ pub fn select(args: CommandArgs<'caller>) -> Result, Shell let objects = args .input - .iter() - .map(|item| Value::Object(select_fields(item, &fields))) - .map(|item| ReturnValue::Value(item)) - .collect(); + .map(move |item| Value::Object(select_fields(&item, &fields))) + .map(|item| ReturnValue::Value(item)); - Ok(objects) + let stream = Pin::new(Box::new(objects)); + Ok(stream) } diff --git a/src/commands/skip.rs b/src/commands/skip.rs index f1059adce7..0ff5ca1267 100644 --- a/src/commands/skip.rs +++ b/src/commands/skip.rs @@ -1,21 +1,13 @@ use crate::errors::ShellError; use crate::prelude::*; -pub fn skip(args: CommandArgs<'caller>) -> Result, ShellError> { +pub fn skip(args: CommandArgs) -> Result { let amount = args.args[0].as_int()?; - let amount = if args.input.len() > amount as usize { - amount as usize - } else { - args.input.len() - }; + let input = args.input; - let out: VecDeque = args - .input - .into_iter() - .skip(amount) + Ok(input + .skip(amount as u64) .map(|v| ReturnValue::Value(v)) - .collect(); - - Ok(out) + .boxed()) } diff --git a/src/commands/sort_by.rs b/src/commands/sort_by.rs index 0f289fdfdf..13663d88a1 100644 --- a/src/commands/sort_by.rs +++ b/src/commands/sort_by.rs @@ -1,23 +1,26 @@ use crate::errors::ShellError; use crate::prelude::*; +use futures::stream::BoxStream; -pub fn sort_by(args: CommandArgs<'caller>) -> Result, ShellError> { +pub fn sort_by(args: CommandArgs) -> Result { let fields: Result, _> = args.args.iter().map(|a| a.as_string()).collect(); let fields = fields?; - let mut output = args.input.into_iter().collect::>(); + let output = args.input.collect::>(); - output.sort_by_key(|item| { - fields - .iter() - .map(|f| item.get_data_by_key(f).borrow().copy()) - .collect::>() + let output = output.map(move |mut vec| { + vec.sort_by_key(|item| { + fields + .iter() + .map(|f| item.get_data_by_key(f).borrow().copy()) + .collect::>() + }); + + vec.into_iter() + .map(|v| ReturnValue::Value(v.copy())) + .collect::>() + .boxed() }); - let output = output - .iter() - .map(|o| ReturnValue::Value(o.copy())) - .collect(); - - Ok(output) + Ok(output.flatten_stream().boxed()) } diff --git a/src/commands/take.rs b/src/commands/take.rs index 540ec4204d..2352f16b2f 100644 --- a/src/commands/take.rs +++ b/src/commands/take.rs @@ -3,21 +3,13 @@ use crate::prelude::*; // TODO: "Amount remaining" wrapper -pub fn take(args: CommandArgs<'caller>) -> Result, ShellError> { +pub fn take(args: CommandArgs) -> Result { let amount = args.args[0].as_int()?; - let amount = if args.input.len() > amount as usize { - amount as usize - } else { - args.input.len() - }; + let input = args.input; - let out: VecDeque = args - .input - .into_iter() - .take(amount) + Ok(input + .take(amount as u64) .map(|v| ReturnValue::Value(v)) - .collect(); - - Ok(out) + .boxed()) } diff --git a/src/commands/to_array.rs b/src/commands/to_array.rs index 6ae59714d5..93a0272973 100644 --- a/src/commands/to_array.rs +++ b/src/commands/to_array.rs @@ -1,15 +1,17 @@ -use crate::errors::ShellError; use crate::object::Value; use crate::prelude::*; -pub fn to_array(args: CommandArgs<'caller>) -> Result, ShellError> { - let out = args.input.into_iter().collect(); - Ok(ReturnValue::single(Value::List(out))) +pub fn to_array(args: CommandArgs) -> Result { + let out = args.input.collect(); + Ok(out + .map(|vec: Vec<_>| single_output(Value::List(vec))) + .flatten_stream() + .boxed()) } -crate fn stream_to_array(stream: VecDeque) -> VecDeque { - let out = Value::List(stream.into_iter().collect()); +crate async fn stream_to_array(stream: InputStream) -> InputStream { + let out = Value::List(stream.collect().await); let mut stream = VecDeque::new(); stream.push_back(out); - stream + stream.boxed() as InputStream } diff --git a/src/commands/view.rs b/src/commands/view.rs index 728fa93623..0c6b5526a5 100644 --- a/src/commands/view.rs +++ b/src/commands/view.rs @@ -2,14 +2,14 @@ use crate::errors::ShellError; use crate::prelude::*; use prettyprint::PrettyPrinter; -pub fn view(args: CommandArgs<'caller>) -> Result, ShellError> { +pub fn view(args: CommandArgs) -> Result { let target = match args.args.first() { // TODO: This needs better infra None => return Err(ShellError::string(format!("cat must take one arg"))), Some(v) => v.as_string()?.clone(), }; - let cwd = args.env.cwd().to_path_buf(); + let cwd = args.env.lock().unwrap().cwd().to_path_buf(); let printer = PrettyPrinter::default() .line_numbers(false) @@ -22,5 +22,5 @@ pub fn view(args: CommandArgs<'caller>) -> Result, ShellEr let _ = printer.file(file.display().to_string()); - Ok(VecDeque::new()) + Ok(VecDeque::new().boxed()) } diff --git a/src/commands/where_.rs b/src/commands/where_.rs index 17f53e61bc..3db875f756 100644 --- a/src/commands/where_.rs +++ b/src/commands/where_.rs @@ -2,26 +2,27 @@ use crate::errors::ShellError; use crate::object::base::find; use crate::prelude::*; -pub fn r#where(args: CommandArgs<'caller>) -> Result, ShellError> { +pub fn r#where(args: CommandArgs) -> Result { if args.args.is_empty() { return Err(ShellError::string("select requires a field")); } let field: Result = args.args[0].as_string(); let field = field?; + let input = args.input; + let operator = args.args[1].copy(); - match args.args[1] { - Value::Primitive(Primitive::Operator(ref operator)) => { - let objects = args - .input - .iter() - .filter(|item| find(&item, &field, operator, &args.args[2])) - .map(|item| ReturnValue::Value(item.copy())) - .collect(); + match operator { + Value::Primitive(Primitive::Operator(operator)) => { + let right = args.args[2].copy(); - Ok(objects) + let objects = input + .filter(move |item| futures::future::ready(find(&item, &field, &operator, &right))) + .map(|item| ReturnValue::Value(item.copy())); + + Ok(objects.boxed()) } - ref x => { + x => { println!("{:?}", x); Err(ShellError::string("expected a comparison operator")) } diff --git a/src/context.rs b/src/context.rs index bcbb30afcc..6f49f6b279 100644 --- a/src/context.rs +++ b/src/context.rs @@ -5,16 +5,16 @@ use std::sync::Arc; pub struct Context { commands: indexmap::IndexMap>, - crate host: Box, - crate env: Environment, + crate host: Arc>, + crate env: Arc>, } impl Context { crate fn basic() -> Result> { Ok(Context { commands: indexmap::IndexMap::new(), - host: Box::new(crate::env::host::BasicHost), - env: Environment::basic()?, + host: Arc::new(Mutex::new(crate::env::host::BasicHost)), + env: Arc::new(Mutex::new(Environment::basic()?)), }) } @@ -36,11 +36,11 @@ impl Context { &mut self, command: Arc, arg_list: Vec, - input: VecDeque, - ) -> Result, ShellError> { + input: InputStream, + ) -> Result { let command_args = CommandArgs { - host: &mut self.host, - env: &self.env, + host: self.host.clone(), + env: self.env.clone(), args: arg_list, input, }; diff --git a/src/format/entries.rs b/src/format/entries.rs index 02845c0854..c619f98b15 100644 --- a/src/format/entries.rs +++ b/src/format/entries.rs @@ -50,8 +50,10 @@ pub struct EntriesListView { } impl EntriesListView { - crate fn from_stream(values: VecDeque) -> EntriesListView { - EntriesListView { values } + crate async fn from_stream(values: InputStream) -> EntriesListView { + EntriesListView { + values: values.collect().await, + } } } diff --git a/src/main.rs b/src/main.rs index 6cf266b286..3f08472912 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,6 @@ #![feature(crate_visibility_modifier)] #![feature(in_band_lifetimes)] +#![feature(async_await)] mod cli; mod commands; @@ -16,5 +17,6 @@ mod stream; use std::error::Error; fn main() -> Result<(), Box> { - crate::cli::cli() + futures::executor::block_on(crate::cli::cli()); + Ok(()) } diff --git a/src/parser/parse.rs b/src/parser/parse.rs index 6ad5807a0d..c379eab6d4 100644 --- a/src/parser/parse.rs +++ b/src/parser/parse.rs @@ -17,7 +17,7 @@ pub enum Item { Operator(Operator), } -#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] pub enum Operator { Equal, NotEqual, diff --git a/src/prelude.rs b/src/prelude.rs index 0480c26932..ebf084ad9f 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -4,4 +4,10 @@ crate use crate::context::Context; crate use crate::env::{Environment, Host}; crate use crate::errors::ShellError; crate use crate::object::{Primitive, Value}; +#[allow(unused)] +crate use crate::stream::{empty_stream, single_output, InputStream, OutputStream}; +#[allow(unused)] +crate use futures::{Future, FutureExt, Stream, StreamExt}; crate use std::collections::VecDeque; +crate use std::pin::Pin; +crate use std::sync::{Arc, Mutex}; diff --git a/src/stream.rs b/src/stream.rs index e69de29bb2..dc4def8cba 100644 --- a/src/stream.rs +++ b/src/stream.rs @@ -0,0 +1,16 @@ +use crate::prelude::*; +use futures::stream::BoxStream; + +pub type InputStream = BoxStream<'static, Value>; +pub type OutputStream = BoxStream<'static, ReturnValue>; + +crate fn empty_stream() -> OutputStream { + VecDeque::new().boxed() +} + +crate fn single_output(item: Value) -> OutputStream { + let value = ReturnValue::Value(item); + let mut vec = VecDeque::new(); + vec.push_back(value); + vec.boxed() +} From bf332ea50c5a1c1ca0ce58ceb402b6023d5cf618 Mon Sep 17 00:00:00 2001 From: Yehuda Katz Date: Thu, 23 May 2019 21:34:43 -0700 Subject: [PATCH 3/5] Improved streams --- Cargo.lock | 1 + Cargo.toml | 1 + src/cli.rs | 94 +++++++++++++++++++++++++++++++++-------- src/commands/command.rs | 8 +--- src/commands/sort_by.rs | 1 - src/context.rs | 2 +- src/env/host.rs | 10 ++++- src/errors.rs | 9 ++++ src/main.rs | 3 +- src/prelude.rs | 2 +- 10 files changed, 100 insertions(+), 31 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 25d84b06b5..cc5805dbf0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -916,6 +916,7 @@ dependencies = [ "derive-new 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)", "dunce 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "futures-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-sink-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)", "indexmap 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "nom 5.0.0-beta1 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/Cargo.toml b/Cargo.toml index 02ae4a3a60..46662611e0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,6 +28,7 @@ ordered-float = "1.0.2" prettyprint = "0.6.0" cursive = { version = "0.12.0", features = ["pancurses-backend"], default-features = false } futures-preview = "0.3.0-alpha.16" +futures-sink-preview = "0.3.0-alpha.16" [dependencies.pancurses] version = "0.16" diff --git a/src/cli.rs b/src/cli.rs index afa320e3c8..15ee6c499f 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -49,12 +49,13 @@ pub async fn cli() -> Result<(), Box> { use crate::commands::*; context.add_commands(vec![ + ("format-list", Arc::new(format_list)), ("ps", Arc::new(ps::ps)), ("ls", Arc::new(ls::ls)), ("cd", Arc::new(cd::cd)), ("view", Arc::new(view::view)), ("skip", Arc::new(skip::skip)), - ("take", Arc::new(take::take)), + ("first", Arc::new(take::take)), ("select", Arc::new(select::select)), ("reject", Arc::new(reject::reject)), ("to-array", Arc::new(to_array::to_array)), @@ -105,6 +106,30 @@ enum LineResult { FatalError(ShellError), } +impl std::ops::Try for LineResult { + type Ok = Option; + type Error = ShellError; + + fn into_result(self) -> Result, ShellError> { + match self { + LineResult::Success(s) => Ok(Some(s)), + LineResult::Error(s) => Err(ShellError::string(s)), + LineResult::Break => Ok(None), + LineResult::FatalError(err) => Err(err), + } + } + fn from_error(v: ShellError) -> Self { + LineResult::Error(v.to_string()) + } + + fn from_ok(v: Option) -> Self { + match v { + None => LineResult::Break, + Some(v) => LineResult::Success(v), + } + } +} + async fn process_line(readline: Result, ctx: &mut Context) -> LineResult { match &readline { Ok(line) if line.trim() == "exit" => LineResult::Break, @@ -137,10 +162,12 @@ async fn process_line(readline: Result, ctx: &mut Context if equal_shapes(&input_vec) { let array = crate::commands::stream_to_array(input_vec.boxed()).await; let args = CommandArgs::from_context(ctx, vec![], array); - format(args).await; + let mut result = format(args); + let mut vec = vec![]; + vec.send_all(&mut result).await?; } else { let args = CommandArgs::from_context(ctx, vec![], input_vec.boxed()); - format(args).await; + format(args).collect::>().await; } } @@ -197,26 +224,57 @@ fn classify_command( } } -async fn format(args: CommandArgs) -> OutputStream { - let input: Vec<_> = args.input.collect().await; - let last = input.len() - 1; - for (i, item) in input.iter().enumerate() { - let view = GenericView::new(item); - crate::format::print_view(&view, &mut *args.host.lock().unwrap()); +crate fn format(args: CommandArgs) -> OutputStream { + let host = args.host.clone(); + let input = args.input.map(|a| a.copy()); + let input = input.collect::>(); - if last != i { - println!(""); - } - } + input + .then(move |input| { + let last = input.len() - 1; + let mut host = host.lock().unwrap(); + for (i, item) in input.iter().enumerate() { + let view = GenericView::new(item); + crate::format::print_view(&view, &mut *host); - empty_stream() + if last != i { + println!(""); + } + } + + futures::future::ready(empty_stream()) + }) + .flatten_stream() + .boxed() } -async fn format_list(args: CommandArgs) -> OutputStream { - let view = EntriesListView::from_stream(args.input).await; - crate::format::print_view(&view, &mut *args.host.lock().unwrap()); +crate fn format_list(args: CommandArgs) -> Result { + let host = args.host.clone(); + // let input = args.input.map(|a| a.copy()); + // let input = input.collect::>(); - empty_stream() + let view = EntriesListView::from_stream(args.input); + + Ok(view + .then(move |view| { + crate::format::print_view(&view, &mut *host.lock().unwrap()); + + futures::future::ready(empty_stream()) + }) + .flatten_stream() + .boxed()) + + // Ok(empty_stream()) + // Ok(args + // .input + // .map(|input| { + // let view = EntriesListView::from_stream(input); + // crate::format::print_view(&view, &mut *args.host.lock().unwrap()); + // }) + // .collect() + // .then(|_| empty_stream()) + // .flatten_stream() + // .boxed()) } fn equal_shapes(input: &VecDeque) -> bool { diff --git a/src/commands/command.rs b/src/commands/command.rs index 71479ceb0f..81848a7c07 100644 --- a/src/commands/command.rs +++ b/src/commands/command.rs @@ -4,7 +4,7 @@ use crate::prelude::*; use std::path::PathBuf; pub struct CommandArgs { - pub host: Arc>, + pub host: Arc>, pub env: Arc>, pub args: Vec, pub input: InputStream, @@ -37,12 +37,6 @@ pub enum ReturnValue { } impl ReturnValue { - crate fn single(value: Value) -> VecDeque { - let mut v = VecDeque::new(); - v.push_back(ReturnValue::Value(value)); - v - } - crate fn change_cwd(path: PathBuf) -> ReturnValue { ReturnValue::Action(CommandAction::ChangeCwd(path)) } diff --git a/src/commands/sort_by.rs b/src/commands/sort_by.rs index 13663d88a1..d00b0dc604 100644 --- a/src/commands/sort_by.rs +++ b/src/commands/sort_by.rs @@ -1,6 +1,5 @@ use crate::errors::ShellError; use crate::prelude::*; -use futures::stream::BoxStream; pub fn sort_by(args: CommandArgs) -> Result { let fields: Result, _> = args.args.iter().map(|a| a.as_string()).collect(); diff --git a/src/context.rs b/src/context.rs index 6f49f6b279..6bea845ca4 100644 --- a/src/context.rs +++ b/src/context.rs @@ -5,7 +5,7 @@ use std::sync::Arc; pub struct Context { commands: indexmap::IndexMap>, - crate host: Arc>, + crate host: Arc>, crate env: Arc>, } diff --git a/src/env/host.rs b/src/env/host.rs index 736c6bb056..7ab034733c 100644 --- a/src/env/host.rs +++ b/src/env/host.rs @@ -17,10 +17,16 @@ crate struct BasicHost; impl Host for BasicHost { fn stdout(&mut self, out: &str) { - println!("{}", out) + match out { + "\n" => println!(""), + other => println!("{}", other), + } } fn stderr(&mut self, out: &str) { - eprintln!("{}", out) + match out { + "\n" => eprintln!(""), + other => eprintln!("{}", other), + } } } diff --git a/src/errors.rs b/src/errors.rs index eb270f30e1..6cee99ebcf 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -42,3 +42,12 @@ impl std::convert::From for ShellError { } } } + +impl std::convert::From for ShellError { + fn from(_input: futures_sink::VecSinkError) -> ShellError { + ShellError { + title: format!("Unexpected Vec Sink Error"), + error: Value::nothing(), + } + } +} diff --git a/src/main.rs b/src/main.rs index 3f08472912..45da4ec084 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,7 @@ #![feature(crate_visibility_modifier)] #![feature(in_band_lifetimes)] #![feature(async_await)] +#![feature(try_trait)] mod cli; mod commands; @@ -17,6 +18,6 @@ mod stream; use std::error::Error; fn main() -> Result<(), Box> { - futures::executor::block_on(crate::cli::cli()); + futures::executor::block_on(crate::cli::cli())?; Ok(()) } diff --git a/src/prelude.rs b/src/prelude.rs index ebf084ad9f..7349baccee 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -7,7 +7,7 @@ crate use crate::object::{Primitive, Value}; #[allow(unused)] crate use crate::stream::{empty_stream, single_output, InputStream, OutputStream}; #[allow(unused)] -crate use futures::{Future, FutureExt, Stream, StreamExt}; +crate use futures::{Future, FutureExt, Sink, SinkExt, Stream, StreamExt}; crate use std::collections::VecDeque; crate use std::pin::Pin; crate use std::sync::{Arc, Mutex}; From f9fb353c5ceaa64eea25d8a09d8b21ecc252b146 Mon Sep 17 00:00:00 2001 From: Yehuda Katz Date: Fri, 24 May 2019 00:29:16 -0700 Subject: [PATCH 4/5] Streams are wired up pairwise --- src/cli.rs | 91 +++++++++++++++++++++++++++++++------- src/commands/classified.rs | 90 ++++++++++++++++++++++++++++++++++++- src/errors.rs | 9 ++++ src/main.rs | 1 + 4 files changed, 172 insertions(+), 19 deletions(-) diff --git a/src/cli.rs b/src/cli.rs index 15ee6c499f..f7c380b93a 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -1,6 +1,8 @@ use crate::prelude::*; -use crate::commands::classified::{ClassifiedCommand, ExternalCommand, InternalCommand}; +use crate::commands::classified::{ + ClassifiedCommand, ClassifiedInputStream, ExternalCommand, InternalCommand, +}; use crate::context::Context; crate use crate::errors::ShellError; crate use crate::format::{EntriesListView, GenericView}; @@ -11,6 +13,7 @@ use rustyline::error::ReadlineError; use rustyline::{self, ColorMode, Config, Editor}; use std::collections::VecDeque; use std::error::Error; +use std::iter::Iterator; use std::sync::Arc; #[derive(Debug)] @@ -145,18 +148,72 @@ async fn process_line(readline: Result, ctx: &mut Context Ok(val) => val, }; - let parsed = result.1; + let parsed: Result, _> = result + .1 + .into_iter() + .map(|item| classify_command(&item, ctx)) + .collect(); - let mut input: InputStream = VecDeque::new().boxed(); + let parsed = parsed?; - for item in parsed { - input = match process_command(item.clone(), input, ctx).await { - Ok(val) => val, - Err(err) => return LineResult::Error(format!("{}", err.description())), - }; + let mut input = ClassifiedInputStream::new(); + + let mut iter = parsed.into_iter().peekable(); + + loop { + let item: Option = iter.next(); + let next: Option<&ClassifiedCommand> = iter.peek(); + + input = match (item, next) { + (None, _) => break, + + ( + Some(ClassifiedCommand::Internal(left)), + Some(ClassifiedCommand::Internal(_)), + ) => match left.run(ctx, input).await { + Ok(val) => ClassifiedInputStream::from_input_stream(val), + Err(err) => return LineResult::Error(format!("{}", err.description())), + }, + + (Some(ClassifiedCommand::Internal(left)), None) => { + match left.run(ctx, input).await { + Ok(val) => ClassifiedInputStream::from_input_stream(val), + Err(err) => return LineResult::Error(format!("{}", err.description())), + } + } + + ( + Some(ClassifiedCommand::External(left)), + Some(ClassifiedCommand::External(_)), + ) => match left.run(ctx, input, true).await { + Ok(val) => val, + Err(err) => return LineResult::Error(format!("{}", err.description())), + }, + + ( + Some(ClassifiedCommand::Internal(_)), + Some(ClassifiedCommand::External(_)), + ) => unimplemented!(), + + ( + Some(ClassifiedCommand::External(_)), + Some(ClassifiedCommand::Internal(_)), + ) => unimplemented!(), + + (Some(ClassifiedCommand::External(left)), None) => { + match left.run(ctx, input, false).await { + Ok(val) => val, + Err(err) => return LineResult::Error(format!("{}", err.description())), + } + } + } + // input = match process_command(item.clone(), input, ctx).await { + // Ok(val) => val, + // Err(err) => return LineResult::Error(format!("{}", err.description())), + // }; } - let input_vec: VecDeque<_> = input.collect().await; + let input_vec: VecDeque<_> = input.objects.collect().await; if input_vec.len() > 0 { if equal_shapes(&input_vec) { @@ -188,15 +245,15 @@ async fn process_line(readline: Result, ctx: &mut Context } } -async fn process_command( - parsed: Vec, - input: InputStream, - context: &mut Context, -) -> Result { - let command = classify_command(&parsed, context)?; +// async fn process_command( +// parsed: Vec, +// input: InputStream, +// context: &mut Context, +// ) -> Result { +// let command = classify_command(&parsed, context)?; - command.run(input, context).await -} +// command.run(context, input).await +// } fn classify_command( command: &[crate::parser::Item], diff --git a/src/commands/classified.rs b/src/commands/classified.rs index a528b9bd34..acf57d85f5 100644 --- a/src/commands/classified.rs +++ b/src/commands/classified.rs @@ -2,6 +2,34 @@ use crate::prelude::*; use std::sync::Arc; use subprocess::Exec; +crate struct ClassifiedInputStream { + crate objects: InputStream, + crate stdin: Option, +} + +impl ClassifiedInputStream { + crate fn new() -> ClassifiedInputStream { + ClassifiedInputStream { + objects: VecDeque::new().boxed(), + stdin: None, + } + } + + crate fn from_input_stream(stream: InputStream) -> ClassifiedInputStream { + ClassifiedInputStream { + objects: stream, + stdin: None, + } + } + + crate fn from_stdout(stdout: std::fs::File) -> ClassifiedInputStream { + ClassifiedInputStream { + objects: VecDeque::new().boxed(), + stdin: Some(stdout), + } + } +} + crate enum ClassifiedCommand { Internal(InternalCommand), External(ExternalCommand), @@ -10,12 +38,12 @@ crate enum ClassifiedCommand { impl ClassifiedCommand { crate async fn run( self, - input: InputStream, context: &mut Context, + input: ClassifiedInputStream, ) -> Result { match self { ClassifiedCommand::Internal(internal) => { - let result = context.run_command(internal.command, internal.args, input)?; + let result = context.run_command(internal.command, internal.args, input.objects)?; let env = context.env.clone(); let stream = result.filter_map(move |v| match v { @@ -50,7 +78,65 @@ crate struct InternalCommand { crate args: Vec, } +impl InternalCommand { + crate async fn run( + self, + context: &mut Context, + input: ClassifiedInputStream, + ) -> Result { + let result = context.run_command(self.command, self.args, input.objects)?; + let env = context.env.clone(); + + let stream = result.filter_map(move |v| match v { + ReturnValue::Action(action) => match action { + CommandAction::ChangeCwd(cwd) => { + env.lock().unwrap().cwd = cwd; + futures::future::ready(None) + } + }, + + ReturnValue::Value(v) => futures::future::ready(Some(v)), + }); + + Ok(stream.boxed() as InputStream) + } +} + crate struct ExternalCommand { crate name: String, crate args: Vec, } + +impl ExternalCommand { + crate async fn run( + self, + context: &mut Context, + mut input: ClassifiedInputStream, + stream_next: bool, + ) -> Result { + let mut process = Exec::shell(&self.name) + .args(&self.args) + .cwd(context.env.lock().unwrap().cwd()); + + if stream_next { + process = process.stdout(subprocess::Redirection::Pipe) + } + + if let Some(stdin) = input.stdin { + process = process.stdin(stdin); + } + + let mut popen = process.popen().unwrap(); + + if stream_next { + match &popen.stdout { + None => unreachable!(), + Some(stdout) => Ok(ClassifiedInputStream::from_stdout(stdout.try_clone()?)), + } + } else { + popen.stdin.take(); + popen.wait()?; + Ok(ClassifiedInputStream::new()) + } + } +} diff --git a/src/errors.rs b/src/errors.rs index 6cee99ebcf..b18f3ff55e 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -51,3 +51,12 @@ impl std::convert::From for ShellError { } } } + +impl std::convert::From for ShellError { + fn from(input: subprocess::PopenError) -> ShellError { + ShellError { + title: format!("{}", input), + error: Value::nothing(), + } + } +} diff --git a/src/main.rs b/src/main.rs index 45da4ec084..8a5da7c13f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,6 +2,7 @@ #![feature(in_band_lifetimes)] #![feature(async_await)] #![feature(try_trait)] +#![feature(bind_by_move_pattern_guards)] mod cli; mod commands; From 9f8d2a4de52b189b251824a4114fe03e751ca5af Mon Sep 17 00:00:00 2001 From: Yehuda Katz Date: Fri, 24 May 2019 11:48:33 -0700 Subject: [PATCH 5/5] Pipe external to internal Each line is a string object --- Cargo.lock | 123 +++++++++++++++++++++++++++++++++++++ Cargo.toml | 7 ++- src/cli.rs | 13 ++-- src/commands/classified.rs | 51 +++++++++++---- src/env/host.rs | 19 ++++++ src/format.rs | 13 +--- src/format/entries.rs | 41 ++++++++----- src/format/generic.rs | 19 +++--- src/format/list.rs | 8 +-- src/format/table.rs | 30 ++++++--- src/object.rs | 1 + src/object/base.rs | 22 +++---- src/object/desc.rs | 101 ++++++++++++++++++++++++++++-- src/object/dict.rs | 25 ++++---- src/object/types.rs | 15 ++++- 15 files changed, 391 insertions(+), 97 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index cc5805dbf0..d9da44decb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -143,6 +143,15 @@ name = "byteorder" version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "bytes" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "cc" version = "1.0.37" @@ -285,6 +294,15 @@ dependencies = [ "crossbeam-utils 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "crossbeam-deque" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "crossbeam-epoch 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "crossbeam-epoch" version = "0.3.1" @@ -299,6 +317,27 @@ dependencies = [ "scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "crossbeam-epoch" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "arrayvec 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "memoffset 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "crossbeam-queue" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "crossbeam-utils" version = "0.2.2" @@ -650,6 +689,11 @@ name = "fuchsia-cprng" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "futures" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "futures-channel-preview" version = "0.3.0-alpha.16" @@ -710,6 +754,7 @@ name = "futures-util-preview" version = "0.3.0-alpha.16" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ + "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", "futures-channel-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)", "futures-core-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)", "futures-io-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)", @@ -717,6 +762,16 @@ dependencies = [ "memchr 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "pin-utils 0.1.0-alpha.4 (registry+https://github.com/rust-lang/crates.io-index)", "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "futures_codec" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -742,6 +797,15 @@ name = "indexmap" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "iovec" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "itertools" version = "0.8.0" @@ -917,6 +981,7 @@ dependencies = [ "dunce 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "futures-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)", "futures-sink-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)", + "futures_codec 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "indexmap 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "nom 5.0.0-beta1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -927,6 +992,8 @@ dependencies = [ "rustyline 4.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "subprocess 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)", "sysinfo 0.8.4 (registry+https://github.com/rust-lang/crates.io-index)", + "term 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-fs 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1633,6 +1700,51 @@ dependencies = [ "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "tokio-executor" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-fs" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-threadpool 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-io" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-threadpool" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "crossbeam-deque 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-queue 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "num_cpus 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", + "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-executor 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "toml" version = "0.5.1" @@ -1787,6 +1899,7 @@ dependencies = [ "checksum build_const 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "39092a32794787acd8525ee150305ff051b0aa6cc2abaf193924f5ab05425f39" "checksum byte-unit 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6754bb4703aa167bed5381f0c6842f1cc31a9ecde3b9443f726dde3ad3afb841" "checksum byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a019b10a2a7cdeb292db131fc8113e57ea2a908f6e7894b0c3c671893b65dbeb" +"checksum bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)" = "206fdffcfa2df7cbe15601ef46c813fce0965eb3286db6b56c583b814b51c81c" "checksum cc 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)" = "39f75544d7bbaf57560d2168f28fd649ff9c76153874db88bdbdfd839b1a7e7d" "checksum cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "b486ce3ccf7ffd79fdeb678eac06a9e6c09fc88d33836340becb8fffe87c5e33" "checksum chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "45912881121cb26fad7c38c17ba7daa18764771836b34fab7d3fbd93ed633878" @@ -1803,7 +1916,10 @@ dependencies = [ "checksum crc32fast 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ba125de2af0df55319f41944744ad91c71113bf74a4646efff39afe1f6842db1" "checksum crossbeam-channel 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "0f0ed1a4de2235cabda8558ff5840bffb97fcb64c97827f354a451307df5f72b" "checksum crossbeam-deque 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f739f8c5363aca78cfb059edf753d8f0d36908c348f3d8d1503f03d8b75d9cf3" +"checksum crossbeam-deque 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b18cd2e169ad86297e6bc0ad9aa679aee9daa4f19e8163860faf7c164e4f5a71" "checksum crossbeam-epoch 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "927121f5407de9956180ff5e936fe3cf4324279280001cd56b669d28ee7e9150" +"checksum crossbeam-epoch 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "04c9e3102cc2d69cd681412141b390abd55a362afc1540965dad0ad4d34280b4" +"checksum crossbeam-queue 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7c979cd6cfe72335896575c6b5688da489e420d36a27a0b9eb0c73db574b4a4b" "checksum crossbeam-utils 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2760899e32a1d58d5abb31129f8fae5de75220bc2176e77ff7c627ae45c918d9" "checksum crossbeam-utils 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "677d453a17e8bd2b913fa38e8b9cf04bcdbb5be790aa294f2389661d72036015" "checksum crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "f8306fcef4a7b563b76b7dd949ca48f52bc1141aa067d2ea09565f3e2652aa5c" @@ -1842,6 +1958,7 @@ dependencies = [ "checksum flate2 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)" = "f87e68aa82b2de08a6e037f1385455759df6e445a8df5e005b4297191dbf18aa" "checksum fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2fad85553e09a6f881f739c29f0b00b0f01357c743266d478b68951ce23285f3" "checksum fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" +"checksum futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)" = "a2037ec1c6c1c4f79557762eab1f7eae1f64f6cb418ace90fae88f0942b60139" "checksum futures-channel-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)" = "4cd523712fc272e9b714669165a2832debee5a5b7e409bfccdc7c0d5cd0cf07a" "checksum futures-core-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)" = "719770f328642b657b849856bb5a607db9538dd5bb3000122e5ead55d0a58c36" "checksum futures-executor-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)" = "315dc58c908535d059576a329b86cd185933433382cfcd394fb2fa353330de03" @@ -1849,10 +1966,12 @@ dependencies = [ "checksum futures-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)" = "fcfeac5f016a4b5835bb93eb7961f50a64f0e001207562703d9ddf4109d7b263" "checksum futures-sink-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)" = "49dcfdacd6b5974ca0b9b78bc38ffd1071da0206179735c3df82e279f5b784e4" "checksum futures-util-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)" = "f7a0451b9c5047c2b9ab93425ffd0793165511e93c04b977cd45fbd41c6e34b2" +"checksum futures_codec 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b60f48aa03e365df015d2fbf0b79f17b440350c268a5e20305da17b394adcc1e" "checksum hashbrown 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "570178d5e4952010d138b0f1d581271ff3a02406d990f887d1e87e3d6e43b0ac" "checksum humantime 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3ca7e5f2e110db35f93b837c81797f3714500b81d517bf20c431b16d3ca4f114" "checksum ident_case 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" "checksum indexmap 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7e81a7c05f79578dbc15793d8b619db9ba32b4577003ef3af1a91c416798c58d" +"checksum iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dbe6e417e7d0975db6512b90796e8ce223145ac4e33c377e4a42882a0e88bb08" "checksum itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5b8467d9c1cebe26feb08c640139247fac215782d35371ade9a2136ed6085358" "checksum itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "501266b7edd0174f8530248f87f99c88fbe60ca4ef3dd486835b8d8d53136f7f" "checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" @@ -1954,6 +2073,10 @@ dependencies = [ "checksum textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" "checksum thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b" "checksum time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "db8dcfca086c1143c9270ac42a2bbd8a7ee477b78ac8e45b19abfb0cbede4b6f" +"checksum tokio-executor 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "83ea44c6c0773cc034771693711c35c677b4b5a4b21b9e7071704c54de7d555e" +"checksum tokio-fs 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "3fe6dc22b08d6993916647d108a1a7d15b9cd29c4f4496c62b92c45b5041b7af" +"checksum tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "5090db468dad16e1a7a54c8c67280c5e4b544f3d3e018f0b913b400261f85926" +"checksum tokio-threadpool 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "72558af20be886ea124595ea0f806dd5703b8958e4705429dd58b3d8231f72f2" "checksum toml 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b8c96d7873fa7ef8bdeb3a9cda3ac48389b4154f32b9803b4bc26220b677b039" "checksum ucd-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "535c204ee4d8434478593480b8f86ab45ec9aae0e83c568ca81abf0fd0e88f86" "checksum unicode-segmentation 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1967f4cdfc355b37fd76d2a954fb2ed3871034eb4f26d60537d88795cfc332a9" diff --git a/Cargo.toml b/Cargo.toml index 46662611e0..6011395ced 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,9 +27,12 @@ byte-unit = "2.1.0" ordered-float = "1.0.2" prettyprint = "0.6.0" cursive = { version = "0.12.0", features = ["pancurses-backend"], default-features = false } -futures-preview = "0.3.0-alpha.16" +futures-preview = { version = "0.3.0-alpha.16", features = ["compat", "io-compat"] } futures-sink-preview = "0.3.0-alpha.16" +tokio-fs = "0.1.6" +futures_codec = "0.2.2" +term = "0.5.2" [dependencies.pancurses] version = "0.16" -features = ["win32a"] \ No newline at end of file +features = ["win32a"] diff --git a/src/cli.rs b/src/cli.rs index f7c380b93a..cee37ef4e7 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -1,7 +1,7 @@ use crate::prelude::*; use crate::commands::classified::{ - ClassifiedCommand, ClassifiedInputStream, ExternalCommand, InternalCommand, + ClassifiedCommand, ClassifiedInputStream, ExternalCommand, InternalCommand, StreamNext, }; use crate::context::Context; crate use crate::errors::ShellError; @@ -185,7 +185,7 @@ async fn process_line(readline: Result, ctx: &mut Context ( Some(ClassifiedCommand::External(left)), Some(ClassifiedCommand::External(_)), - ) => match left.run(ctx, input, true).await { + ) => match left.run(ctx, input, StreamNext::External).await { Ok(val) => val, Err(err) => return LineResult::Error(format!("{}", err.description())), }, @@ -196,12 +196,15 @@ async fn process_line(readline: Result, ctx: &mut Context ) => unimplemented!(), ( - Some(ClassifiedCommand::External(_)), + Some(ClassifiedCommand::External(left)), Some(ClassifiedCommand::Internal(_)), - ) => unimplemented!(), + ) => match left.run(ctx, input, StreamNext::Internal).await { + Ok(val) => val, + Err(err) => return LineResult::Error(format!("{}", err.description())), + }, (Some(ClassifiedCommand::External(left)), None) => { - match left.run(ctx, input, false).await { + match left.run(ctx, input, StreamNext::Last).await { Ok(val) => val, Err(err) => return LineResult::Error(format!("{}", err.description())), } diff --git a/src/commands/classified.rs b/src/commands/classified.rs index acf57d85f5..5778e647cf 100644 --- a/src/commands/classified.rs +++ b/src/commands/classified.rs @@ -1,4 +1,6 @@ use crate::prelude::*; +use futures::compat::AsyncRead01CompatExt; +use futures_codec::{Framed, LinesCodec}; use std::sync::Arc; use subprocess::Exec; @@ -107,20 +109,29 @@ crate struct ExternalCommand { crate args: Vec, } +crate enum StreamNext { + Last, + External, + Internal, +} + impl ExternalCommand { crate async fn run( self, context: &mut Context, mut input: ClassifiedInputStream, - stream_next: bool, + stream_next: StreamNext, ) -> Result { let mut process = Exec::shell(&self.name) .args(&self.args) .cwd(context.env.lock().unwrap().cwd()); - if stream_next { - process = process.stdout(subprocess::Redirection::Pipe) - } + let mut process = match stream_next { + StreamNext::Last => process, + StreamNext::External | StreamNext::Internal => { + process.stdout(subprocess::Redirection::Pipe) + } + }; if let Some(stdin) = input.stdin { process = process.stdin(stdin); @@ -128,15 +139,31 @@ impl ExternalCommand { let mut popen = process.popen().unwrap(); - if stream_next { - match &popen.stdout { - None => unreachable!(), - Some(stdout) => Ok(ClassifiedInputStream::from_stdout(stdout.try_clone()?)), + match stream_next { + StreamNext::Last => { + popen.wait()?; + Ok(ClassifiedInputStream::new()) + } + StreamNext::External => { + let stdout = popen.stdout.take().unwrap(); + Ok(ClassifiedInputStream::from_stdout(stdout)) + } + StreamNext::Internal => { + let stdout = popen.stdout.take().unwrap(); + let file = futures::io::AllowStdIo::new(stdout); + let stream = Framed::new(file, LinesCodec {}); + let stream = stream.map(|line| Value::string(line.unwrap())); + Ok(ClassifiedInputStream::from_input_stream(stream.boxed())) } - } else { - popen.stdin.take(); - popen.wait()?; - Ok(ClassifiedInputStream::new()) } + + // if stream_next { + // let stdout = popen.stdout.take().unwrap(); + // Ok(ClassifiedInputStream::from_stdout(stdout)) + // } else { + // // popen.stdin.take(); + // popen.wait()?; + // Ok(ClassifiedInputStream::new()) + // } } } diff --git a/src/env/host.rs b/src/env/host.rs index 7ab034733c..57f6064181 100644 --- a/src/env/host.rs +++ b/src/env/host.rs @@ -1,9 +1,20 @@ pub trait Host { + fn out_terminal(&self) -> Box; + fn err_terminal(&self) -> Box; + fn stdout(&mut self, out: &str); fn stderr(&mut self, out: &str); } impl Host for Box { + fn out_terminal(&self) -> Box { + (**self).out_terminal() + } + + fn err_terminal(&self) -> Box { + (**self).err_terminal() + } + fn stdout(&mut self, out: &str) { (**self).stdout(out) } @@ -16,6 +27,14 @@ impl Host for Box { crate struct BasicHost; impl Host for BasicHost { + fn out_terminal(&self) -> Box { + term::stdout().unwrap() + } + + fn err_terminal(&self) -> Box { + term::stderr().unwrap() + } + fn stdout(&mut self, out: &str) { match out { "\n" => println!(""), diff --git a/src/format.rs b/src/format.rs index 4b0b9a4445..8494697e64 100644 --- a/src/format.rs +++ b/src/format.rs @@ -10,16 +10,9 @@ crate use generic::GenericView; crate use table::TableView; crate trait RenderView { - fn render_view(&self, host: &dyn Host) -> Vec; + fn render_view(&self, host: &mut dyn Host) -> Result<(), ShellError>; } -fn print_rendered(lines: &[String], host: &mut dyn Host) { - for line in lines { - host.stdout(line); - } -} - -crate fn print_view(view: &impl RenderView, host: &mut Host) { - // let mut ctx = context.lock().unwrap(); - crate::format::print_rendered(&view.render_view(host), host); +crate fn print_view(view: &impl RenderView, host: &mut dyn Host) -> Result<(), ShellError> { + view.render_view(host) } diff --git a/src/format/entries.rs b/src/format/entries.rs index c619f98b15..8f9ee83936 100644 --- a/src/format/entries.rs +++ b/src/format/entries.rs @@ -10,7 +10,7 @@ use derive_new::new; // another_name : ... #[derive(new)] pub struct EntriesView { - entries: Vec<(String, String)>, + entries: Vec<(crate::object::DescriptorName, String)>, } impl EntriesView { @@ -31,17 +31,28 @@ impl EntriesView { } impl RenderView for EntriesView { - fn render_view(&self, _host: &dyn Host) -> Vec { + fn render_view(&self, _host: &mut dyn Host) -> Result<(), ShellError> { if self.entries.len() == 0 { - return vec![]; + return Ok(()); } - let max_name_size: usize = self.entries.iter().map(|(n, _)| n.len()).max().unwrap(); - - self.entries + let max_name_size: usize = self + .entries .iter() - .map(|(k, v)| format!("{:width$} : {}", k, v, width = max_name_size)) - .collect() + .map(|(n, _)| n.display().len()) + .max() + .unwrap(); + + for (name, value) in &self.entries { + println!( + "{:width$} : {}", + name.display(), + value, + width = max_name_size + ) + } + + Ok(()) } } @@ -58,26 +69,22 @@ impl EntriesListView { } impl RenderView for EntriesListView { - fn render_view(&self, host: &dyn Host) -> Vec { + fn render_view(&self, host: &mut dyn Host) -> Result<(), ShellError> { if self.values.len() == 0 { - return vec![]; + return Ok(()); } - let mut strings = vec![]; - let last = self.values.len() - 1; for (i, item) in self.values.iter().enumerate() { let view = EntriesView::from_value(item); - let out = view.render_view(host); - - strings.extend(out); + view.render_view(host); if i != last { - strings.push("\n".to_string()); + host.stdout("\n"); } } - strings + Ok(()) } } diff --git a/src/format/generic.rs b/src/format/generic.rs index e7bb198a45..45f78af8df 100644 --- a/src/format/generic.rs +++ b/src/format/generic.rs @@ -10,17 +10,17 @@ pub struct GenericView<'value> { } impl RenderView for GenericView<'value> { - fn render_view(&self, host: &dyn Host) -> Vec { + fn render_view(&self, host: &mut dyn Host) -> Result<(), ShellError> { match self.value { - Value::Primitive(p) => vec![p.format(None)], + Value::Primitive(p) => Ok(host.stdout(&p.format(None))), Value::List(l) => { let view = TableView::from_list(l); if let Some(view) = view { - view.render_view(host) - } else { - vec![] + view.render_view(host); } + + Ok(()) // let mut list: Vec = vec![]; // for item in l { // match item { @@ -39,11 +39,14 @@ impl RenderView for GenericView<'value> { o @ Value::Object(_) => { let view = EntriesView::from_value(o); - let out = view.render_view(host); - out + view.render_view(host); + Ok(()) } - Value::Error(e) => vec![format!("{}", e)], + Value::Error(e) => { + host.stdout(&format!("{:?}", e)); + Ok(()) + } } } } diff --git a/src/format/list.rs b/src/format/list.rs index 508dc7c804..a33f2078e7 100644 --- a/src/format/list.rs +++ b/src/format/list.rs @@ -11,14 +11,12 @@ pub struct ListView { } impl RenderView for ListView { - fn render_view(&self, _host: &dyn Host) -> Vec { - let mut out = vec![]; - + fn render_view(&self, host: &mut dyn Host) -> Result<(), ShellError> { for output in &self.list { let string: String = output.iter().map(|l| format!("{}\n", l)).collect(); - out.push(format!("{}{}", string, self.sep)); + host.stdout(&format!("{}{}", string, self.sep)); } - out + Ok(()) } } diff --git a/src/format/table.rs b/src/format/table.rs index 8014bf3c7a..1981018ef7 100644 --- a/src/format/table.rs +++ b/src/format/table.rs @@ -1,8 +1,9 @@ use crate::format::RenderView; use crate::object::Value; use crate::prelude::*; +use ansi_term::Color; use derive_new::new; -use prettytable::{Cell, Row, Table}; +use prettytable::{color, Attr, Cell, Row, Table}; // An entries list is printed like this: // @@ -24,7 +25,7 @@ impl TableView { let item = &values[0]; let descs = item.data_descriptors(); - let headers: Vec = descs.iter().map(|d| d.name.clone()).collect(); + let headers: Vec = descs.iter().map(|d| d.name.display().to_string()).collect(); let mut entries = vec![]; @@ -43,13 +44,27 @@ impl TableView { } impl RenderView for TableView { - fn render_view(&self, _host: &dyn Host) -> Vec { + fn render_view(&self, host: &mut dyn Host) -> Result<(), ShellError> { if self.entries.len() == 0 { - return vec![]; + return Ok(()); } let mut table = Table::new(); - let header: Vec = self.headers.iter().map(|h| Cell::new(h)).collect(); + + // let format = prettytable::format::FormatBuilder::new(); + // .column_separator(Color::Black.bold().paint("|")); + + table.set_format(*prettytable::format::consts::FORMAT_NO_COLSEP); + + let header: Vec = self + .headers + .iter() + .map(|h| { + Cell::new(h) + .with_style(Attr::ForegroundColor(color::GREEN)) + .with_style(Attr::Bold) + }) + .collect(); table.add_row(Row::new(header)); @@ -57,9 +72,8 @@ impl RenderView for TableView { table.add_row(Row::new(row.iter().map(|h| Cell::new(h)).collect())); } - let mut out = vec![]; - table.print(&mut out).unwrap(); + table.print_term(&mut *host.out_terminal()).unwrap(); - vec![String::from_utf8_lossy(&out).to_string()] + Ok(()) } } diff --git a/src/object.rs b/src/object.rs index fc01dc2bca..1b6557d544 100644 --- a/src/object.rs +++ b/src/object.rs @@ -6,5 +6,6 @@ crate mod process; crate mod types; crate use base::{Primitive, Value}; +crate use desc::{DataDescriptor, DescriptorName}; crate use dict::Dictionary; crate use files::dir_entry_dict; diff --git a/src/object/base.rs b/src/object/base.rs index b3cc291f2a..d915844346 100644 --- a/src/object/base.rs +++ b/src/object/base.rs @@ -1,5 +1,5 @@ use crate::errors::ShellError; -use crate::object::desc::DataDescriptor; +use crate::object::{DataDescriptor, DescriptorName}; use crate::parser::parse::Operator; use crate::prelude::*; use ansi_term::Color; @@ -69,7 +69,7 @@ pub enum Value { impl Value { crate fn data_descriptors(&self) -> Vec { match self { - Value::Primitive(_) => vec![], + Value::Primitive(_) => vec![DataDescriptor::value_of()], Value::Object(o) => o.data_descriptors(), Value::List(_) => vec![], Value::Error(_) => vec![], @@ -87,7 +87,7 @@ impl Value { crate fn get_data(&'a self, desc: &DataDescriptor) -> MaybeOwned<'a, Value> { match self { - Value::Primitive(_) => MaybeOwned::Owned(Value::nothing()), + p @ Value::Primitive(_) => MaybeOwned::Borrowed(p), Value::Object(o) => o.get_data(desc), Value::List(_) => MaybeOwned::Owned(Value::nothing()), Value::Error(_) => MaybeOwned::Owned(Value::nothing()), @@ -203,9 +203,9 @@ crate fn select_fields(obj: &Value, fields: &[String]) -> crate::object::Diction let descs = obj.data_descriptors(); for field in fields { - match descs.iter().find(|d| d.name == *field) { - None => out.add(field.to_string(), Value::nothing()), - Some(desc) => out.add(field.to_string(), obj.get_data(desc).borrow().copy()), + match descs.iter().find(|d| d.name.is_string(field)) { + None => out.add(DataDescriptor::for_string_name(field), Value::nothing()), + Some(desc) => out.add(desc.copy(), obj.get_data(desc).borrow().copy()), } } @@ -218,10 +218,10 @@ crate fn reject_fields(obj: &Value, fields: &[String]) -> crate::object::Diction let descs = obj.data_descriptors(); for desc in descs { - if fields.contains(&desc.name) { - continue; - } else { - out.add(desc.name.clone(), obj.get_data(&desc).borrow().copy()) + match desc.name.as_string() { + None => continue, + Some(s) if fields.iter().any(|field| field == s) => continue, + Some(s) => out.add(desc.copy(), obj.get_data(&desc).borrow().copy()), } } @@ -230,7 +230,7 @@ crate fn reject_fields(obj: &Value, fields: &[String]) -> crate::object::Diction crate fn find(obj: &Value, field: &str, op: &Operator, rhs: &Value) -> bool { let descs = obj.data_descriptors(); - match descs.iter().find(|d| d.name == *field) { + match descs.iter().find(|d| d.name.is_string(field)) { None => false, Some(desc) => { let v = obj.get_data(desc).borrow().copy(); diff --git a/src/object/desc.rs b/src/object/desc.rs index 21c11c874b..a7def78f04 100644 --- a/src/object/desc.rs +++ b/src/object/desc.rs @@ -1,17 +1,110 @@ -use crate::object::types::Type; +use crate::object::types::{AnyShell, Type}; use derive_new::new; -#[derive(new)] +#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] +pub enum DescriptorName { + String(String), + ValueOf, +} + +impl DescriptorName { + crate fn display(&self) -> &str { + match self { + DescriptorName::String(s) => s, + DescriptorName::ValueOf => "value", + } + } + + crate fn as_string(&self) -> Option<&str> { + match self { + DescriptorName::String(s) => Some(s), + DescriptorName::ValueOf => None, + } + } + + crate fn is_string(&self, string: &str) -> bool { + match self { + DescriptorName::String(s) => s == string, + DescriptorName::ValueOf => false, + } + } +} + +#[derive(Debug, new)] pub struct DataDescriptor { - crate name: String, + crate name: DescriptorName, crate readonly: bool, crate ty: Box, } +impl From<&str> for DataDescriptor { + fn from(input: &str) -> DataDescriptor { + DataDescriptor { + name: DescriptorName::String(input.to_string()), + readonly: true, + ty: Box::new(AnyShell), + } + } +} + +impl From for DataDescriptor { + fn from(input: String) -> DataDescriptor { + DataDescriptor { + name: DescriptorName::String(input), + readonly: true, + ty: Box::new(AnyShell), + } + } +} + impl PartialEq for DataDescriptor { fn eq(&self, other: &DataDescriptor) -> bool { self.name == other.name && self.readonly == other.readonly && self.ty.equal(&*other.ty) } } -impl DataDescriptor {} +impl std::hash::Hash for DataDescriptor { + fn hash(&self, state: &mut H) { + self.name.hash(state); + self.readonly.hash(state); + self.ty.id().hash(state); + } +} + +impl Eq for DataDescriptor {} + +impl DescriptorName { + crate fn for_string_name(name: impl Into) -> DescriptorName { + DescriptorName::String(name.into()) + } +} + +impl DataDescriptor { + crate fn value_of() -> DataDescriptor { + DataDescriptor { + name: DescriptorName::ValueOf, + readonly: true, + ty: Box::new(AnyShell), + } + } + + crate fn for_name(name: impl Into) -> DataDescriptor { + DataDescriptor { + name: name.into(), + readonly: true, + ty: Box::new(AnyShell), + } + } + + crate fn for_string_name(name: impl Into) -> DataDescriptor { + DataDescriptor::for_name(DescriptorName::for_string_name(name)) + } + + crate fn copy(&self) -> DataDescriptor { + DataDescriptor { + name: self.name.clone(), + readonly: self.readonly, + ty: self.ty.copy(), + } + } +} diff --git a/src/object/dict.rs b/src/object/dict.rs index 52f981701b..1e2379304b 100644 --- a/src/object/dict.rs +++ b/src/object/dict.rs @@ -1,13 +1,13 @@ use crate::prelude::*; -use crate::object::desc::DataDescriptor; +use crate::object::{DataDescriptor, DescriptorName}; use crate::object::{Primitive, Value}; use indexmap::IndexMap; use std::cmp::{Ordering, PartialOrd}; #[derive(Debug, Default, Eq, PartialEq)] pub struct Dictionary { - entries: IndexMap, + entries: IndexMap, } impl PartialOrd for Dictionary { @@ -41,7 +41,7 @@ impl PartialEq for Dictionary { } impl Dictionary { - crate fn add(&mut self, name: impl Into, value: Value) { + crate fn add(&mut self, name: impl Into, value: Value) { self.entries.insert(name.into(), value); } @@ -49,31 +49,30 @@ impl Dictionary { let mut out = Dictionary::default(); for (key, value) in self.entries.iter() { - out.add(key.clone(), value.copy()); + out.add(key.copy(), value.copy()); } out } crate fn data_descriptors(&self) -> Vec { - self.entries - .iter() - .map(|(name, _)| { - DataDescriptor::new(name.clone(), true, Box::new(crate::object::types::AnyShell)) - }) - .collect() + self.entries.iter().map(|(name, _)| name.copy()).collect() } crate fn get_data(&'a self, desc: &DataDescriptor) -> MaybeOwned<'a, Value> { - match self.entries.get(&desc.name) { + match self.entries.get(desc) { Some(v) => MaybeOwned::Borrowed(v), None => MaybeOwned::Owned(Value::Primitive(Primitive::Nothing)), } } crate fn get_data_by_key(&self, name: &str) -> MaybeOwned<'_, Value> { - match self.entries.get(name) { - Some(v) => MaybeOwned::Borrowed(v), + match self + .entries + .iter() + .find(|(desc_name, _)| desc_name.name.is_string(name)) + { + Some((_, v)) => MaybeOwned::Borrowed(v), None => MaybeOwned::Owned(Value::Primitive(Primitive::Nothing)), } } diff --git a/src/object/types.rs b/src/object/types.rs index 3673346661..4edd0203a4 100644 --- a/src/object/types.rs +++ b/src/object/types.rs @@ -1,11 +1,14 @@ use std::any::Any; +use std::fmt::Debug; -pub trait Type { +pub trait Type: Debug + Send { fn as_any(&self) -> &dyn Any; fn equal(&self, other: &dyn Type) -> bool; + fn id(&self) -> u64; + fn copy(&self) -> Box; } -#[derive(Eq, PartialEq)] +#[derive(Debug, Eq, PartialEq)] pub struct AnyShell; impl Type for AnyShell { @@ -16,4 +19,12 @@ impl Type for AnyShell { fn equal(&self, other: &dyn Type) -> bool { other.as_any().is::() } + + fn id(&self) -> u64 { + 0 + } + + fn copy(&self) -> Box { + Box::new(AnyShell) + } }