Basic pipelining is working!

This commit is contained in:
Yehuda Katz 2019-05-15 11:12:38 -05:00
parent 975ff7c2fb
commit 3040638881
18 changed files with 405 additions and 239 deletions

View File

@ -1,100 +0,0 @@
cargo install cargo-edit
ls
ls | foo | bar
ls
ls | ap
ls ab cd
ls ab cd | de
ls ab cd | grep 1243
ls ab cd|grep 1243
ls
hello world
hello world | zomg
ls
ls | zomgt
ls | zomg
ls soms
ls something
ls something | grep
dir
npm --help
cd target
dir
ls
cd target
cd ..
dir
cd target
cd ..
cd target
cd ..
ls
dir
cd target
ls
cd ..
ls
cd target
cd ..
cd target
cd ..
cd target
cd ..
cd target
cd ..
cd target
cd ..
cd target
cd ..
cd target
cd ..
ls
cd target
cd ..
cargo build
cargo build --verbose
ls
cargo
cargo build
cargo run
git status
git add .
git commit
git push
git status
git add .
git commit
git push
git config --global core.autocrlf input
git config
ls
cd target
ls
cd target
cd ..
cd target
ls
git status
git add .
git commit
git push
cd target
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

View File

@ -3,6 +3,8 @@ crate mod cd;
crate mod command; crate mod command;
crate mod ls; crate mod ls;
crate mod ps; crate mod ps;
crate mod take;
crate mod to_array; crate mod to_array;
crate use command::Command; crate use command::Command;
crate use to_array::to_array;

View File

@ -12,10 +12,12 @@ pub trait CommandBlueprint {
) -> Result<Box<dyn Command>, ShellError>; ) -> Result<Box<dyn Command>, ShellError>;
} }
#[derive(Debug)]
pub enum CommandAction { pub enum CommandAction {
ChangeCwd(PathBuf), ChangeCwd(PathBuf),
} }
#[derive(Debug)]
pub enum ReturnValue { pub enum ReturnValue {
Value(Value), Value(Value),
Action(CommandAction), Action(CommandAction),

View File

@ -9,9 +9,7 @@ use std::rc::Rc;
use sysinfo::SystemExt; use sysinfo::SystemExt;
#[derive(new)] #[derive(new)]
pub struct PsBlueprint { pub struct PsBlueprint;
system: Rc<RefCell<sysinfo::System>>,
}
impl crate::CommandBlueprint for PsBlueprint { impl crate::CommandBlueprint for PsBlueprint {
fn create( fn create(
@ -20,18 +18,16 @@ impl crate::CommandBlueprint for PsBlueprint {
host: &dyn crate::Host, host: &dyn crate::Host,
env: &mut crate::Environment, env: &mut crate::Environment,
) -> Result<Box<dyn Command>, ShellError> { ) -> Result<Box<dyn Command>, ShellError> {
Ok(Box::new(Ps::new(self.system.clone()))) Ok(Box::new(Ps::new()))
} }
} }
#[derive(new)] #[derive(new)]
pub struct Ps { pub struct Ps;
system: Rc<RefCell<sysinfo::System>>,
}
impl crate::Command for Ps { impl crate::Command for Ps {
fn run(&mut self, stream: VecDeque<Value>) -> Result<VecDeque<ReturnValue>, ShellError> { fn run(&mut self, stream: VecDeque<Value>) -> Result<VecDeque<ReturnValue>, ShellError> {
let mut system = self.system.borrow_mut(); let mut system = sysinfo::System::new();
system.refresh_all(); system.refresh_all();
let list = system.get_process_list(); let list = system.get_process_list();
@ -41,7 +37,6 @@ impl crate::Command for Ps {
.map(|(_, process)| { .map(|(_, process)| {
ReturnValue::Value(Value::Object(Box::new(Process::new(process.clone())))) ReturnValue::Value(Value::Object(Box::new(Process::new(process.clone()))))
}) })
.take(5)
.collect::<VecDeque<_>>(); .collect::<VecDeque<_>>();
Ok(list) Ok(list)

51
src/commands/take.rs Normal file
View File

@ -0,0 +1,51 @@
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 TakeBlueprint;
impl crate::CommandBlueprint for TakeBlueprint {
fn create(
&self,
args: Vec<Value>,
host: &dyn Host,
env: &mut Environment,
) -> Result<Box<dyn Command>, ShellError> {
if args.is_empty() {
return Err(ShellError::string("take requires an integer"));
}
let amount = args[0].as_int()?;
Ok(Box::new(Take { amount }))
}
}
#[derive(new)]
pub struct Take {
amount: i64,
}
impl crate::Command for Take {
fn run(&mut self, stream: VecDeque<Value>) -> Result<VecDeque<ReturnValue>, ShellError> {
let amount = if stream.len() > self.amount as usize {
self.amount as usize
} else {
stream.len()
};
let out: VecDeque<ReturnValue> = stream
.into_iter()
.take(amount)
.map(|v| ReturnValue::Value(v))
.collect();
Ok(out)
}
}

View File

@ -30,3 +30,10 @@ impl crate::Command for ToArray {
Ok(ReturnValue::single(Value::List(out))) Ok(ReturnValue::single(Value::List(out)))
} }
} }
crate fn to_array(stream: VecDeque<Value>) -> VecDeque<Value> {
let out = Value::List(stream.into_iter().collect());
let mut stream = VecDeque::new();
stream.push_back(out);
stream
}

46
src/context.rs Normal file
View File

@ -0,0 +1,46 @@
use crate::prelude::*;
use std::collections::BTreeMap;
use std::error::Error;
pub type Commands = BTreeMap<String, Box<dyn crate::CommandBlueprint>>;
pub struct Context {
commands: BTreeMap<String, Box<dyn crate::CommandBlueprint>>,
crate host: Box<dyn crate::Host>,
crate env: Environment,
}
impl Context {
crate fn basic() -> Result<Context, Box<Error>> {
Ok(Context {
commands: BTreeMap::new(),
host: Box::new(crate::env::host::BasicHost),
env: crate::Environment::basic()?,
})
}
pub fn add_commands(&mut self, commands: Vec<(&str, Box<dyn crate::CommandBlueprint>)>) {
for (name, command) in commands {
self.commands.insert(name.to_string(), command);
}
}
crate fn get_command(&mut self, name: &str) -> Option<&dyn crate::CommandBlueprint> {
self.commands.get(name).map(|c| &**c)
}
crate fn has_command(&mut self, name: &str) -> bool {
self.commands.contains_key(name)
}
crate fn create_command(
&mut self,
name: &str,
arg_list: Vec<Value>,
) -> Result<Box<dyn Command>, ShellError> {
match self.commands.get(name) {
Some(command) => Ok(command.create(arg_list, &self.host, &mut self.env)?),
None => Err(ShellError::string(format!("Missing command {}", name))),
}
}
}

View File

@ -6,7 +6,7 @@ crate mod table;
use crate::object::Value; use crate::object::Value;
use crate::prelude::*; use crate::prelude::*;
crate use entries::EntriesView; crate use entries::{EntriesListView, EntriesView};
crate use generic::GenericView; crate use generic::GenericView;
crate use list::ListView; crate use list::ListView;
crate use table::TableView; crate use table::TableView;

View File

@ -1,5 +1,8 @@
use crate::format::RenderView; use crate::format::RenderView;
use crate::object::base::ToEntriesView;
use crate::prelude::*;
use crate::Host; use crate::Host;
use derive_new::new; use derive_new::new;
// An entries list is printed like this: // An entries list is printed like this:
@ -26,3 +29,38 @@ impl RenderView for EntriesView {
.collect() .collect()
} }
} }
pub struct EntriesListView {
values: VecDeque<Value>,
}
impl EntriesListView {
crate fn from_stream(values: VecDeque<Value>) -> EntriesListView {
EntriesListView { values }
}
}
impl RenderView for EntriesListView {
fn render_view(&self, host: &dyn Host) -> Vec<String> {
if self.values.len() == 0 {
return vec![];
}
let mut strings = vec![];
let last = self.values.len() - 1;
for (i, item) in self.values.iter().enumerate() {
let view = item.to_entries_view();
let out = view.render_view(host);
strings.extend(out);
if i != last {
strings.push("\n".to_string());
}
}
strings
}
}

View File

@ -3,6 +3,7 @@
#![allow(unused)] #![allow(unused)]
mod commands; mod commands;
mod context;
mod env; mod env;
mod errors; mod errors;
mod format; mod format;
@ -13,11 +14,12 @@ mod prelude;
crate use crate::commands::args::{Args, Streams}; crate use crate::commands::args::{Args, Streams};
use crate::commands::command::ReturnValue; use crate::commands::command::ReturnValue;
crate use crate::commands::command::{Command, CommandAction, CommandBlueprint}; crate use crate::commands::command::{Command, CommandAction, CommandBlueprint};
use crate::context::Context;
crate use crate::env::{Environment, Host}; crate use crate::env::{Environment, Host};
crate use crate::errors::ShellError; crate use crate::errors::ShellError;
crate use crate::format::RenderView; crate use crate::format::{EntriesListView, RenderView};
use crate::object::base::{ToEntriesView, ToGenericView}; use crate::object::base::{ToEntriesView, ToGenericView};
use crate::object::Value; use crate::object::{ShellObject, Value};
use ansi_term::Color; use ansi_term::Color;
use conch_parser::lexer::Lexer; use conch_parser::lexer::Lexer;
@ -29,6 +31,7 @@ use std::collections::BTreeMap;
use std::collections::VecDeque; use std::collections::VecDeque;
use std::error::Error; use std::error::Error;
use std::rc::Rc; use std::rc::Rc;
use std::sync::{Arc, Mutex};
use subprocess::Exec; use subprocess::Exec;
use sysinfo::{self, SystemExt}; use sysinfo::{self, SystemExt};
@ -47,51 +50,30 @@ impl<T> MaybeOwned<'a, T> {
} }
} }
type Commands = BTreeMap<String, Box<dyn crate::CommandBlueprint>>;
struct Context {
commands: BTreeMap<String, Box<dyn crate::CommandBlueprint>>,
host: Box<dyn crate::Host>,
env: Environment,
}
impl Context {
fn basic() -> Result<Context, Box<Error>> {
Ok(Context {
commands: BTreeMap::new(),
host: Box::new(crate::env::host::BasicHost),
env: crate::Environment::basic()?,
})
}
}
fn main() -> Result<(), Box<Error>> { fn main() -> Result<(), Box<Error>> {
let mut rl = Editor::<()>::new(); let mut rl = Editor::<()>::new();
if rl.load_history("history.txt").is_err() { if rl.load_history("history.txt").is_err() {
println!("No previous history."); println!("No previous history.");
} }
let mut context = Context::basic()?; let mut context = Arc::new(Mutex::new(Context::basic()?));
// let mut commands = BTreeMap::<String, Box<dyn crate::CommandBlueprint>>::new(); {
use crate::commands::*;
let mut system = Rc::new(RefCell::new(sysinfo::System::new())); context.lock().unwrap().add_commands(vec![
let mut ps = crate::commands::ps::PsBlueprint::new(system); ("ps", Box::new(ps::PsBlueprint)),
let mut ls = crate::commands::ls::LsBlueprint; ("ls", Box::new(ls::LsBlueprint)),
let mut cd = crate::commands::cd::CdBlueprint; ("cd", Box::new(cd::CdBlueprint)),
let mut to_array = crate::commands::to_array::ToArrayBlueprint; ("take", Box::new(take::TakeBlueprint)),
("to-array", Box::new(to_array::ToArrayBlueprint)),
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 { loop {
let readline = rl.readline(&format!( let readline = rl.readline(&format!(
"{}> ", "{}> ",
Color::Green.paint(context.env.cwd().display().to_string()) Color::Green.paint(context.lock().unwrap().env.cwd().display().to_string())
)); ));
match readline { match readline {
@ -105,16 +87,23 @@ fn main() -> Result<(), Box<Error>> {
let mut input = VecDeque::new(); let mut input = VecDeque::new();
let last = parsed.len() - 1;
for item in parsed { for item in parsed {
// println!("Processing {:?}", item);
input = process_command( input = process_command(
crate::parser::print_items(&item), crate::parser::print_items(&item),
item.clone(), item.clone(),
input, input,
&mut context, context.clone(),
)?; )?;
}
// println!("OUTPUT: {:?}", input); if input.len() > 0 {
if equal_shapes(&input) {
format(crate::commands::to_array(input), context.clone());
} else {
format(input, context.clone());
}
} }
} }
Err(ReadlineError::Interrupted) => { Err(ReadlineError::Interrupted) => {
@ -140,31 +129,30 @@ fn process_command(
line: String, line: String,
parsed: Vec<crate::parser::Item>, parsed: Vec<crate::parser::Item>,
input: VecDeque<Value>, input: VecDeque<Value>,
context: &mut Context, context: Arc<Mutex<Context>>,
) -> Result<VecDeque<Value>, ShellError> { ) -> Result<VecDeque<Value>, ShellError> {
let command = &parsed[0].name(); let command = &parsed[0].name();
let arg_list = parsed[1..] let arg_list = parsed[1..].iter().map(|i| i.as_value()).collect();
.iter()
.map(|i| Value::string(i.name().to_string()))
.collect();
let streams = Streams::new(); let streams = Streams::new();
// let args = Args::new(arg_list); if command == &"format" {
format(input, context);
match *command { Ok(VecDeque::new())
"format" => { } else if command == &"format-list" {
for item in input { let view = EntriesListView::from_stream(input);
let view = item.to_generic_view(); let mut ctx = context.lock().unwrap();
crate::format::print_rendered(&view.render_view(&context.host), &mut context.host);
}
Ok(VecDeque::new()) crate::format::print_rendered(&view.render_view(&ctx.host), &mut ctx.host);
}
command => match context.commands.get_mut(command) { Ok(VecDeque::new())
Some(command) => { } else {
let mut instance = command.create(arg_list, &context.host, &mut context.env)?; let mut ctx = context.lock().unwrap();
match ctx.has_command(*command) {
true => {
let mut instance = ctx.create_command(command, arg_list)?;
let mut result = instance.run(input)?; let mut result = instance.run(input)?;
let mut next = VecDeque::new(); let mut next = VecDeque::new();
@ -172,7 +160,7 @@ fn process_command(
for v in result { for v in result {
match v { match v {
ReturnValue::Action(action) => match action { ReturnValue::Action(action) => match action {
crate::CommandAction::ChangeCwd(cwd) => context.env.cwd = cwd, crate::CommandAction::ChangeCwd(cwd) => ctx.env.cwd = cwd,
}, },
ReturnValue::Value(v) => next.push_back(v), ReturnValue::Value(v) => next.push_back(v),
@ -182,10 +170,43 @@ fn process_command(
Ok(next) Ok(next)
} }
other => { false => {
Exec::shell(line).cwd(context.env.cwd()).join().unwrap(); Exec::shell(line).cwd(ctx.env.cwd()).join().unwrap();
Ok(VecDeque::new()) Ok(VecDeque::new())
} }
}, }
} }
} }
fn format(input: VecDeque<Value>, context: Arc<Mutex<Context>>) {
let mut ctx = context.lock().unwrap();
let last = input.len() - 1;
for (i, item) in input.iter().enumerate() {
let view = item.to_generic_view();
crate::format::print_rendered(&view.render_view(&ctx.host), &mut ctx.host);
if last != i {
println!("");
}
}
}
fn equal_shapes(input: &VecDeque<Value>) -> 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
}

View File

@ -42,6 +42,7 @@ impl ShellObject for Value {
Value::List(l) => format!("[list List]"), Value::List(l) => format!("[list List]"),
} }
} }
fn data_descriptors(&self) -> Vec<DataDescriptor> { fn data_descriptors(&self) -> Vec<DataDescriptor> {
match self { match self {
Value::Primitive(p) => vec![], Value::Primitive(p) => vec![],
@ -80,6 +81,17 @@ impl Value {
} }
} }
crate fn as_int(&self) -> Result<i64, ShellError> {
match self {
Value::Primitive(Primitive::Int(i)) => Ok(*i),
// TODO: this should definitely be more general with better errors
other => Err(ShellError::string(format!(
"Expected integer, got {:?}",
other
))),
}
}
crate fn string(s: impl Into<String>) -> Value { crate fn string(s: impl Into<String>) -> Value {
Value::Primitive(Primitive::String(s.into())) Value::Primitive(Primitive::String(s.into()))
} }
@ -115,7 +127,10 @@ pub trait ToEntriesView {
fn to_entries_view(&self) -> EntriesView; fn to_entries_view(&self) -> EntriesView;
} }
impl ToEntriesView for ShellObject { impl<T> ToEntriesView for T
where
T: ShellObject,
{
fn to_entries_view(&self) -> EntriesView { fn to_entries_view(&self) -> EntriesView {
let descs = self.data_descriptors(); let descs = self.data_descriptors();
let mut entries = vec![]; let mut entries = vec![];
@ -136,6 +151,19 @@ impl ToEntriesView for ShellObject {
} }
} }
impl ShellObject for Box<dyn ShellObject> {
fn to_shell_string(&self) -> String {
(**self).to_shell_string()
}
fn data_descriptors(&self) -> Vec<DataDescriptor> {
(**self).data_descriptors()
}
fn get_data(&'a self, desc: &DataDescriptor) -> crate::MaybeOwned<'a, Value> {
(**self).get_data(desc)
}
}
pub trait ToGenericView { pub trait ToGenericView {
fn to_generic_view(&self) -> GenericView; fn to_generic_view(&self) -> GenericView;
} }

View File

@ -1,4 +1,4 @@
use crate::object::types::{Any, Type}; use crate::object::types::{AnyShell, Type};
use derive_new::new; use derive_new::new;
#[derive(new)] #[derive(new)]
@ -8,12 +8,18 @@ pub struct DataDescriptor {
crate ty: Box<dyn Type>, crate ty: Box<dyn Type>,
} }
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 DataDescriptor {
crate fn any(name: impl Into<String>) -> DataDescriptor { crate fn any(name: impl Into<String>) -> DataDescriptor {
DataDescriptor { DataDescriptor {
name: name.into(), name: name.into(),
readonly: true, readonly: true,
ty: Box::new(Any), ty: Box::new(AnyShell),
} }
} }
} }

View File

@ -26,7 +26,7 @@ impl crate::object::ShellObject for Dictionary {
self.entries self.entries
.iter() .iter()
.map(|(name, value)| { .map(|(name, value)| {
DataDescriptor::new(name.clone(), true, Box::new(crate::object::types::Any)) DataDescriptor::new(name.clone(), true, Box::new(crate::object::types::AnyShell))
}) })
.collect() .collect()
} }
@ -59,7 +59,7 @@ impl crate::object::ShellObject for ScopedDictionary<'parent> {
self.entries self.entries
.iter() .iter()
.map(|(name, value)| { .map(|(name, value)| {
DataDescriptor::new(name.clone(), true, Box::new(crate::object::types::Any)) DataDescriptor::new(name.clone(), true, Box::new(crate::object::types::AnyShell))
}) })
.collect() .collect()
} }

View File

@ -1,5 +1,19 @@
pub trait Type {} use std::any::{Any, TypeId};
pub struct Any; pub trait Type {
fn as_any(&self) -> &dyn Any;
fn equal(&self, other: &dyn Type) -> bool;
}
impl Type for Any {} #[derive(Eq, PartialEq)]
pub struct AnyShell;
impl Type for AnyShell {
fn as_any(&self) -> &dyn Any {
self as &dyn Any
}
fn equal(&self, other: &dyn Type) -> bool {
other.as_any().is::<AnyShell>()
}
}

View File

@ -1,63 +1,4 @@
use nom::branch::alt; crate mod completer;
use nom::bytes::complete::{escaped, is_not, tag}; crate mod parse;
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, Clone)] crate use self::parse::{print_items, shell_parser, Item};
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,
}
}
}
fn esc(s: &str) -> IResult<&str, &str> {
escaped(is_not("\\\""), '\\', one_of("\"n\\"))(s)
}
fn quoted(s: &str) -> IResult<&str, Item> {
terminated(preceded(tag("\""), esc), tag("\""))(s)
.map(|(a, b)| (a, Item::Quoted(b.to_string())))
}
fn unquoted(s: &str) -> IResult<&str, Item> {
is_not(" |")(s).map(|(a, b)| (a, Item::Bare(b.to_string())))
}
fn command_token(s: &str) -> IResult<&str, Item> {
alt((quoted, unquoted))(s)
}
fn command_args(s: &str) -> IResult<&str, Vec<Item>> {
separated_list(tag(" "), command_token)(s)
}
named!(
pub shell_parser(&str) -> Vec<Vec<Item>>,
complete!(
ws!(
separated_list!(tag("|"), command_args)
)
)
);

32
src/parser/completer.rs Normal file
View File

@ -0,0 +1,32 @@
use rustyline::{completion, Context};
use std::collections::BTreeMap;
crate struct Completer {
commands: BTreeMap<String, Box<dyn crate::CommandBlueprint>>,
}
impl completion::Completer for Completer {
type Candidate = completion::Pair;
fn complete(
&self,
line: &str,
pos: usize,
ctx: &Context<'_>,
) -> rustyline::Result<(usize, Vec<completion::Pair>)> {
let pairs = self
.commands
.keys()
.map(|k| completion::Pair {
display: k.clone(),
replacement: k.clone(),
})
.collect();
Ok((0, pairs))
}
fn update(&self, line: &mut rustyline::line_buffer::LineBuffer, start: usize, elected: &str) {
let end = line.pos();
line.replace(start..end, elected)
}
}

82
src/parser/parse.rs Normal file
View File

@ -0,0 +1,82 @@
use crate::prelude::*;
use nom::branch::alt;
use nom::bytes::complete::{escaped, is_a, is_not, tag};
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};
use std::str::FromStr;
#[derive(Debug, Clone)]
pub enum Item {
Quoted(String),
Bare(String),
Int(i64),
}
impl Item {
crate fn as_value(&self) -> Value {
match self {
Item::Quoted(s) => Value::Primitive(Primitive::String(s.clone())),
Item::Bare(s) => Value::Primitive(Primitive::String(s.clone())),
Item::Int(i) => Value::Primitive(Primitive::Int(*i)),
}
}
}
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),
Item::Int(i) => format!("{:?}", i),
});
itertools::join(formatted, " ")
}
impl Item {
crate fn name(&self) -> &str {
match self {
Item::Quoted(s) => s,
Item::Bare(s) => s,
Item::Int(i) => unimplemented!(),
}
}
}
fn esc(s: &str) -> IResult<&str, &str> {
escaped(is_not("\\\""), '\\', one_of("\"n\\"))(s)
}
fn quoted(s: &str) -> IResult<&str, Item> {
terminated(preceded(tag("\""), esc), tag("\""))(s)
.map(|(a, b)| (a, Item::Quoted(b.to_string())))
}
fn unquoted(s: &str) -> IResult<&str, Item> {
is_not(" |")(s).map(|(a, b)| (a, Item::Bare(b.to_string())))
}
fn int(s: &str) -> IResult<&str, Item> {
is_a("1234567890")(s).map(|(a, b)| (a, Item::Int(FromStr::from_str(b).unwrap())))
}
fn command_token(s: &str) -> IResult<&str, Item> {
alt((int, quoted, unquoted))(s)
}
fn command_args(s: &str) -> IResult<&str, Vec<Item>> {
separated_list(tag(" "), command_token)(s)
}
named!(
pub shell_parser(&str) -> Vec<Vec<Item>>,
complete!(
ws!(
separated_list!(tag("|"), command_args)
)
)
);

View File

@ -3,4 +3,5 @@ crate use crate::commands::command::{Command, CommandAction, CommandBlueprint, R
crate use crate::env::{Environment, Host}; crate use crate::env::{Environment, Host};
crate use crate::errors::ShellError; crate use crate::errors::ShellError;
crate use crate::format::RenderView; crate use crate::format::RenderView;
crate use crate::object::{Primitive, Value};
crate use std::collections::VecDeque; crate use std::collections::VecDeque;