From ceb0487ebaeacd9c758dd5c1a5843ef73961dd97 Mon Sep 17 00:00:00 2001 From: Yehuda Katz Date: Mon, 13 May 2019 13:30:51 -0400 Subject: [PATCH] A bunch of rework I'm gonna use a VecDeque now instead of trying to get async streams working to make progress, but the intent is that we should be able to use async streams in and out to interleave the work better. --- Cargo.lock | 17 ++++++++++ Cargo.toml | 2 ++ history.txt | 9 ++++++ src/commands.rs | 2 ++ src/commands/args.rs | 70 +++++++++++++++++++++++++++++++---------- src/commands/cd.rs | 23 +++++++------- src/commands/command.rs | 31 +++++++++++------- src/commands/ls.rs | 18 +++++------ src/commands/ps.rs | 16 +++++----- src/errors.rs | 9 ++++++ src/main.rs | 43 ++++++++++++++++--------- src/object/base.rs | 5 ++- src/prelude.rs | 6 ++++ 13 files changed, 177 insertions(+), 74 deletions(-) create mode 100644 src/prelude.rs diff --git a/Cargo.lock b/Cargo.lock index 7077cc6109..78c80a42d1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -254,6 +254,19 @@ 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-core" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "either 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "itertools" version = "0.8.0" @@ -372,6 +385,8 @@ dependencies = [ "conch-parser 0.1.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)", "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)", "prettytable-rs 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -751,6 +766,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "795bd83d3abeb9220f257e597aa0080a508b27533824adf336529648f6abf7e2" "checksum failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "ea1063915fd7ef4309e222a5a07cf9c319fb9c7836b1f89b85458672dbb127e1" "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 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 lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bc5729f27f159ddd61f4df6228e827e86643d4d3e7c32183cb30a1c08f604a14" diff --git a/Cargo.toml b/Cargo.toml index c2f7aae581..f206bcd7e4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,3 +19,5 @@ conch-parser = "0.1.0" nom = "5.0.0-beta1" subprocess = "0.1.18" dunce = "1.0.0" +futures = "0.1.27" +futures-core = "0.2.1" diff --git a/history.txt b/history.txt index ad50e5da69..cd2e1f2578 100644 --- a/history.txt +++ b/history.txt @@ -81,3 +81,12 @@ cd target cd .. cd target ls +git status +git add . +git commit +git push +cd target +cd .. +exit +ls +ps diff --git a/src/commands.rs b/src/commands.rs index c22d85ac10..01d943d38c 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -3,3 +3,5 @@ crate mod cd; crate mod command; crate mod ls; crate mod ps; + +crate use command::Command; diff --git a/src/commands/args.rs b/src/commands/args.rs index 1cedae9c5f..671567bb12 100644 --- a/src/commands/args.rs +++ b/src/commands/args.rs @@ -1,20 +1,60 @@ use crate::object::Value; +use crate::ShellError; use derive_new::new; use std::cell::Cell; use std::collections::VecDeque; -#[derive(Debug, Default)] -pub struct ObjectStream { - queue: VecDeque, +#[derive(Debug)] +pub enum LogLevel { + Trace, + Debug, + Info, + Warn, + Error, + Fatal, } +#[derive(Debug)] +pub struct LogItem { + level: LogLevel, + value: Value, +} + +#[derive(Debug, Default)] +pub struct ObjectStream { + queue: VecDeque, +} + +impl ObjectStream { + crate fn empty() -> ObjectStream { + ObjectStream { + queue: VecDeque::new(), + } + } + + crate fn iter(&self) -> impl Iterator { + self.queue.iter() + } + + crate fn take(&mut self) -> Option { + self.queue.pop_front() + } + + crate fn add(&mut self, value: T) { + self.queue.push_back(value); + } +} + +#[derive(new)] pub struct Streams { - success: Cell, - // error: ObjectStream, - // warning: ObjectStream, - // debug: ObjectStream, - // trace: ObjectStream, - // verbose: ObjectStream, + #[new(value = "ObjectStream::empty()")] + success: ObjectStream, + + #[new(value = "ObjectStream::empty()")] + errors: ObjectStream, + + #[new(value = "ObjectStream::empty()")] + log: ObjectStream, } impl std::fmt::Debug for Streams { @@ -24,16 +64,12 @@ impl std::fmt::Debug for Streams { } impl Streams { - crate fn new() -> Streams { - Streams { - success: Cell::new(ObjectStream::default()), - } + crate fn read(&mut self) -> Option { + self.success.take() } - crate fn take_success(&mut self) -> Cell { - let new_stream = Cell::new(ObjectStream::default()); - self.success.swap(&new_stream); - new_stream + crate fn add(&mut self, value: Value) { + self.success.add(value); } // fn take_stream(&mut self, stream: &mut ObjectStream) -> ObjectStream { diff --git a/src/commands/cd.rs b/src/commands/cd.rs index 3a9cfc1521..1199d76563 100644 --- a/src/commands/cd.rs +++ b/src/commands/cd.rs @@ -1,6 +1,7 @@ use crate::errors::ShellError; use crate::object::process::Process; use crate::object::{DirEntry, ShellObject, Value}; +use crate::prelude::*; use crate::Args; use derive_new::new; use std::path::{Path, PathBuf}; @@ -12,13 +13,13 @@ pub struct CdBlueprint; impl crate::CommandBlueprint for CdBlueprint { fn create( &self, - args: Args, - host: &dyn crate::Host, - env: &mut crate::Environment, - ) -> Result, ShellError> { + args: Vec, + host: &dyn Host, + env: &mut Environment, + ) -> Result, ShellError> { let target = match args.first() { // TODO: This needs better infra - None => return Err(ShellError::new(format!("cd must take one arg"))), + None => return Err(ShellError::string(format!("cd must take one arg"))), Some(v) => v.as_string()?.clone(), }; @@ -36,12 +37,10 @@ pub struct Cd { } impl crate::Command for Cd { - fn run(&mut self) -> Result { - Ok(crate::CommandSuccess { - value: Value::nothing(), - action: vec![crate::CommandAction::ChangeCwd(dunce::canonicalize( - self.cwd.join(&self.target).as_path(), - )?)], - }) + fn run(&mut self, stream: VecDeque) -> Result, ShellError> { + let mut stream = VecDeque::new(); + let path = dunce::canonicalize(self.cwd.join(&self.target).as_path())?; + stream.push_back(ReturnValue::change_cwd(path)); + Ok(stream) } } diff --git a/src/commands/command.rs b/src/commands/command.rs index d3385465cf..f259901a04 100644 --- a/src/commands/command.rs +++ b/src/commands/command.rs @@ -1,31 +1,38 @@ use crate::errors::ShellError; use crate::object::Value; +use crate::prelude::*; use std::path::PathBuf; pub trait CommandBlueprint { fn create( &self, - input: crate::Args, + input: Vec, host: &dyn crate::Host, env: &mut crate::Environment, ) -> Result, ShellError>; } -crate enum CommandAction { +pub enum CommandAction { ChangeCwd(PathBuf), } -pub struct CommandSuccess { - crate value: Value, - crate action: Vec, +pub enum ReturnValue { + Value(Value), + Action(CommandAction), +} + +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)) + } } pub trait Command { - fn begin(&mut self) -> Result<(), ShellError> { - Ok(()) - } - fn run(&mut self) -> Result; - fn end(&mut self) -> Result<(), ShellError> { - Ok(()) - } + fn run(&mut self, stream: VecDeque) -> Result, ShellError>; } diff --git a/src/commands/ls.rs b/src/commands/ls.rs index 4c34874c1b..4941463b04 100644 --- a/src/commands/ls.rs +++ b/src/commands/ls.rs @@ -1,8 +1,9 @@ use crate::errors::ShellError; use crate::object::process::Process; use crate::object::{DirEntry, ShellObject, Value}; +use crate::prelude::*; use crate::Args; -use crate::{Command, CommandSuccess}; +use crate::Command; use derive_new::new; use std::path::PathBuf; use sysinfo::SystemExt; @@ -13,7 +14,7 @@ pub struct LsBlueprint; impl crate::CommandBlueprint for LsBlueprint { fn create( &self, - args: Args, + args: Vec, host: &dyn crate::Host, env: &mut crate::Environment, ) -> Result, ShellError> { @@ -29,20 +30,17 @@ pub struct Ls { } impl crate::Command for Ls { - fn run(&mut self) -> Result { + fn run(&mut self, stream: VecDeque) -> Result, ShellError> { let entries = - std::fs::read_dir(&self.cwd).map_err((|e| ShellError::new(format!("{:?}", e))))?; + std::fs::read_dir(&self.cwd).map_err((|e| ShellError::string(format!("{:?}", e))))?; - let mut shell_entries = vec![]; + let mut shell_entries = VecDeque::new(); for entry in entries { let value = Value::object(DirEntry::new(entry?)?); - shell_entries.push(value) + shell_entries.push_back(ReturnValue::Value(value)) } - Ok(CommandSuccess { - value: Value::list(shell_entries), - action: vec![], - }) + Ok(shell_entries) } } diff --git a/src/commands/ps.rs b/src/commands/ps.rs index faf2687bbb..559f979c5e 100644 --- a/src/commands/ps.rs +++ b/src/commands/ps.rs @@ -1,6 +1,7 @@ use crate::errors::ShellError; use crate::object::process::Process; use crate::object::{ShellObject, Value}; +use crate::prelude::*; use crate::Command; use derive_new::new; use std::cell::RefCell; @@ -15,7 +16,7 @@ pub struct PsBlueprint { impl crate::CommandBlueprint for PsBlueprint { fn create( &self, - args: crate::Args, + args: Vec, host: &dyn crate::Host, env: &mut crate::Environment, ) -> Result, ShellError> { @@ -29,7 +30,7 @@ pub struct Ps { } impl crate::Command for Ps { - fn run(&mut self) -> Result { + fn run(&mut self, stream: VecDeque) -> Result, ShellError> { let mut system = self.system.borrow_mut(); system.refresh_all(); @@ -37,13 +38,12 @@ impl crate::Command for Ps { let list = list .into_iter() - .map(|(_, process)| Value::Object(Box::new(Process::new(process.clone())))) + .map(|(_, process)| { + ReturnValue::Value(Value::Object(Box::new(Process::new(process.clone())))) + }) .take(5) - .collect(); + .collect::>(); - Ok(crate::CommandSuccess { - value: Value::List(list), - action: vec![], - }) + Ok(list) } } diff --git a/src/errors.rs b/src/errors.rs index efe13dab22..568482eb03 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -1,8 +1,16 @@ +use crate::Value; use derive_new::new; #[derive(Debug, new)] pub struct ShellError { title: String, + error: Value, +} + +impl ShellError { + crate fn string(title: impl Into) -> ShellError { + ShellError::new(title.into(), Value::nothing()) + } } impl std::fmt::Display for ShellError { @@ -17,6 +25,7 @@ impl std::convert::From for ShellError { fn from(input: std::io::Error) -> ShellError { ShellError { title: format!("{}", input), + error: Value::nothing(), } } } diff --git a/src/main.rs b/src/main.rs index 83d892c2ae..63db5b0ef7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -8,9 +8,11 @@ mod errors; mod format; mod object; mod parser; +mod prelude; crate use crate::commands::args::{Args, Streams}; -crate use crate::commands::command::{Command, CommandAction, CommandBlueprint, CommandSuccess}; +use crate::commands::command::ReturnValue; +crate use crate::commands::command::{Command, CommandAction, CommandBlueprint}; crate use crate::env::{Environment, Host}; crate use crate::errors::ShellError; crate use crate::format::RenderView; @@ -24,6 +26,7 @@ use rustyline::error::ReadlineError; use rustyline::Editor; use std::cell::RefCell; use std::collections::BTreeMap; +use std::collections::VecDeque; use std::error::Error; use std::rc::Rc; use subprocess::Exec; @@ -73,7 +76,7 @@ fn main() -> Result<(), Box> { match readline { Ok(line) => { let result = crate::parser::shell_parser(&line) - .map_err(|e| ShellError::new(format!("{:?}", e)))?; + .map_err(|e| ShellError::string(format!("{:?}", e)))?; let parsed = result.1; @@ -89,26 +92,38 @@ fn main() -> Result<(), Box> { .map(|i| Value::string(i.name().to_string())) .collect(); - let args = Args::new(arg_list); + let streams = Streams::new(); + + // let args = Args::new(arg_list); match commands.get_mut(*command) { Some(command) => { - let mut instance = command.create(args, &mut host, &mut env)?; - let result = instance.run()?; + let mut instance = command.create(arg_list, &mut host, &mut env)?; - for action in result.action { - match action { - crate::CommandAction::ChangeCwd(cwd) => env.cwd = cwd, + let out = VecDeque::new(); + + let mut result = instance.run(out)?; + let mut next = VecDeque::new(); + + for v in result { + match v { + ReturnValue::Action(action) => match action { + crate::CommandAction::ChangeCwd(cwd) => env.cwd = cwd, + }, + + ReturnValue::Value(v) => next.push_back(v), } } - let view = result.value.to_generic_view(); - let rendered = view.render_view(&mut host); + for item in next { + let view = item.to_generic_view(); + let rendered = view.render_view(&mut host); - for line in rendered { - match line.as_ref() { - "\n" => println!(""), - line => println!("{}", line), + for line in rendered { + match line.as_ref() { + "\n" => println!(""), + line => println!("{}", line), + } } } } diff --git a/src/object/base.rs b/src/object/base.rs index 4946d89bf5..15c0bf62b4 100644 --- a/src/object/base.rs +++ b/src/object/base.rs @@ -73,7 +73,10 @@ impl Value { Value::Primitive(Primitive::String(s)) => Ok(s.to_string()), // TODO: this should definitely be more general with better errors - other => Err(ShellError::new(format!("Expected string, got {:?}", other))), + other => Err(ShellError::string(format!( + "Expected string, got {:?}", + other + ))), } } diff --git a/src/prelude.rs b/src/prelude.rs new file mode 100644 index 0000000000..ba4f2c44e5 --- /dev/null +++ b/src/prelude.rs @@ -0,0 +1,6 @@ +crate use crate::commands::args::{Args, Streams}; +crate use crate::commands::command::{Command, CommandAction, CommandBlueprint, ReturnValue}; +crate use crate::env::{Environment, Host}; +crate use crate::errors::ShellError; +crate use crate::format::RenderView; +crate use std::collections::VecDeque;