diff --git a/history.txt b/history.txt index cd2e1f2578..5b5eb51df6 100644 --- a/history.txt +++ b/history.txt @@ -1,10 +1,3 @@ -hello -ps -ls -ps -ls -ps -ls cargo install cargo-edit ls ls | foo | bar @@ -90,3 +83,18 @@ cd .. exit ls ps +to-array +exit +ls +dir +ls +cd target +cd .. +ps +ls +dir +ls +ls | to-array +ls | format +ls | to-array | format +git status diff --git a/src/commands.rs b/src/commands.rs index 01d943d38c..c3f07e0da9 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -3,5 +3,6 @@ crate mod cd; crate mod command; crate mod ls; crate mod ps; +crate mod to_array; crate use command::Command; diff --git a/src/commands/to_array.rs b/src/commands/to_array.rs new file mode 100644 index 0000000000..fd02921e89 --- /dev/null +++ b/src/commands/to_array.rs @@ -0,0 +1,32 @@ +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}; +use sysinfo::SystemExt; + +#[derive(new)] +pub struct ToArrayBlueprint; + +impl crate::CommandBlueprint for ToArrayBlueprint { + fn create( + &self, + args: Vec, + host: &dyn Host, + env: &mut Environment, + ) -> Result, ShellError> { + Ok(Box::new(ToArray)) + } +} + +#[derive(new)] +pub struct ToArray; + +impl crate::Command for ToArray { + fn run(&mut self, stream: VecDeque) -> Result, ShellError> { + let out = stream.into_iter().collect(); + Ok(ReturnValue::single(Value::List(out))) + } +} diff --git a/src/env/host.rs b/src/env/host.rs index aea86cabf4..acc474f410 100644 --- a/src/env/host.rs +++ b/src/env/host.rs @@ -2,6 +2,12 @@ pub trait Host { fn stdout(&mut self, out: &str); } +impl Host for Box { + fn stdout(&mut self, out: &str) { + (**self).stdout(out) + } +} + crate struct BasicHost; impl Host for BasicHost { diff --git a/src/format.rs b/src/format.rs index 281f26a18e..7bb02b50ac 100644 --- a/src/format.rs +++ b/src/format.rs @@ -4,7 +4,7 @@ crate mod list; crate mod table; use crate::object::Value; -use crate::Host; +use crate::prelude::*; crate use entries::EntriesView; crate use generic::GenericView; @@ -14,3 +14,9 @@ crate use table::TableView; crate trait RenderView { fn render_view(&self, host: &dyn Host) -> Vec; } + +crate fn print_rendered(lines: &[String], host: &mut dyn Host) { + for line in lines { + host.stdout(line); + } +} diff --git a/src/main.rs b/src/main.rs index 63db5b0ef7..92cde65cb9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -47,30 +47,51 @@ impl MaybeOwned<'a, T> { } } +type Commands = BTreeMap>; + +struct Context { + commands: BTreeMap>, + host: Box, + env: Environment, +} + +impl Context { + fn basic() -> Result> { + Ok(Context { + commands: BTreeMap::new(), + host: Box::new(crate::env::host::BasicHost), + env: crate::Environment::basic()?, + }) + } +} + fn main() -> Result<(), Box> { let mut rl = Editor::<()>::new(); if rl.load_history("history.txt").is_err() { println!("No previous history."); } - let mut host = crate::env::host::BasicHost; - let mut env = crate::Environment::basic()?; + let mut context = Context::basic()?; - let mut commands = BTreeMap::>::new(); + // let mut commands = BTreeMap::>::new(); let mut system = Rc::new(RefCell::new(sysinfo::System::new())); let mut ps = crate::commands::ps::PsBlueprint::new(system); let mut ls = crate::commands::ls::LsBlueprint; let mut cd = crate::commands::cd::CdBlueprint; + let mut to_array = crate::commands::to_array::ToArrayBlueprint; - commands.insert("ps".to_string(), Box::new(ps)); - commands.insert("ls".to_string(), Box::new(ls)); - commands.insert("cd".to_string(), Box::new(cd)); + context.commands.insert("ps".to_string(), Box::new(ps)); + context.commands.insert("ls".to_string(), Box::new(ls)); + context.commands.insert("cd".to_string(), Box::new(cd)); + context + .commands + .insert("to-array".to_string(), Box::new(to_array)); loop { let readline = rl.readline(&format!( "{}> ", - Color::Green.paint(env.cwd().display().to_string()) + Color::Green.paint(context.env.cwd().display().to_string()) )); match readline { @@ -82,55 +103,18 @@ fn main() -> Result<(), Box> { rl.add_history_entry(line.as_ref()); - if parsed.len() > 1 { - println!("Piping is not yet implemented"); - } + let mut input = VecDeque::new(); - let command = &parsed[0][0].name(); - let arg_list = parsed[0][1..] - .iter() - .map(|i| Value::string(i.name().to_string())) - .collect(); + for item in parsed { + // println!("Processing {:?}", item); + input = process_command( + crate::parser::print_items(&item), + item.clone(), + input, + &mut context, + )?; - let streams = Streams::new(); - - // let args = Args::new(arg_list); - - match commands.get_mut(*command) { - Some(command) => { - let mut instance = command.create(arg_list, &mut host, &mut env)?; - - 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), - } - } - - 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), - } - } - } - } - - other => { - Exec::shell(line).cwd(env.cwd()).join().unwrap(); - } + // println!("OUTPUT: {:?}", input); } } Err(ReadlineError::Interrupted) => { @@ -151,3 +135,57 @@ fn main() -> Result<(), Box> { Ok(()) } + +fn process_command( + line: String, + parsed: Vec, + input: VecDeque, + context: &mut Context, +) -> Result, ShellError> { + let command = &parsed[0].name(); + let arg_list = parsed[1..] + .iter() + .map(|i| Value::string(i.name().to_string())) + .collect(); + + let streams = Streams::new(); + + // let args = Args::new(arg_list); + + match *command { + "format" => { + for item in input { + let view = item.to_generic_view(); + crate::format::print_rendered(&view.render_view(&context.host), &mut context.host); + } + + Ok(VecDeque::new()) + } + + command => match context.commands.get_mut(command) { + Some(command) => { + let mut instance = command.create(arg_list, &context.host, &mut context.env)?; + + let mut result = instance.run(input)?; + let mut next = VecDeque::new(); + + for v in result { + match v { + ReturnValue::Action(action) => match action { + crate::CommandAction::ChangeCwd(cwd) => context.env.cwd = cwd, + }, + + ReturnValue::Value(v) => next.push_back(v), + } + } + + Ok(next) + } + + other => { + Exec::shell(line).cwd(context.env.cwd()).join().unwrap(); + Ok(VecDeque::new()) + } + }, + } +} diff --git a/src/parser.rs b/src/parser.rs index 4d60103733..525e70169f 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -1,24 +1,35 @@ use nom::branch::alt; use nom::bytes::complete::{escaped, is_not, tag}; -use nom::{ws, named, separated_list, complete}; use nom::character::complete::one_of; use nom::multi::separated_list; use nom::sequence::{preceded, terminated}; use nom::IResult; +use nom::{complete, named, separated_list, ws}; -#[derive(Debug)] +#[derive(Debug, Clone)] pub enum Item { Quoted(String), Bare(String), } +crate fn print_items(items: &[Item]) -> String { + let mut out = String::new(); + + let formatted = items.iter().map(|item| match item { + Item::Bare(s) => format!("{}", s), + Item::Quoted(s) => format!("{:?}", s), + }); + + itertools::join(formatted, " ") +} + impl Item { - crate fn name(&self) -> &str { - match self { - Item::Quoted(s) => s, - Item::Bare(s) => s + crate fn name(&self) -> &str { + match self { + Item::Quoted(s) => s, + Item::Bare(s) => s, + } } - } } fn esc(s: &str) -> IResult<&str, &str> {