Data flows across commands via streams now

This commit is contained in:
Yehuda Katz 2019-05-23 00:23:06 -07:00
parent 31dd579d6f
commit 625a356361
21 changed files with 171 additions and 157 deletions

1
rustfmt.toml Normal file
View file

@ -0,0 +1 @@
edition = "2018"

View file

@ -1,12 +1,11 @@
use crate::prelude::*; use crate::prelude::*;
use crate::commands::classified::{ClassifiedCommand, ExternalCommand, InternalCommand}; use crate::commands::classified::{ClassifiedCommand, ExternalCommand, InternalCommand};
use crate::commands::command::ReturnValue;
use crate::context::Context; use crate::context::Context;
crate use crate::env::Host;
crate use crate::errors::ShellError; crate use crate::errors::ShellError;
crate use crate::format::{EntriesListView, GenericView}; crate use crate::format::{EntriesListView, GenericView};
use crate::object::Value; use crate::object::Value;
use crate::stream::empty_stream;
use rustyline::error::ReadlineError; use rustyline::error::ReadlineError;
use rustyline::{self, ColorMode, Config, Editor}; use rustyline::{self, ColorMode, Config, Editor};
@ -29,7 +28,7 @@ impl<T> MaybeOwned<'a, T> {
} }
} }
pub fn cli() -> Result<(), Box<Error>> { pub async fn cli() -> Result<(), Box<Error>> {
let config = Config::builder().color_mode(ColorMode::Forced).build(); let config = Config::builder().color_mode(ColorMode::Forced).build();
let h = crate::shell::Helper::new(); let h = crate::shell::Helper::new();
let mut rl: Editor<crate::shell::Helper> = Editor::with_config(config); let mut rl: Editor<crate::shell::Helper> = Editor::with_config(config);
@ -50,8 +49,6 @@ pub fn cli() -> Result<(), Box<Error>> {
use crate::commands::*; use crate::commands::*;
context.add_commands(vec![ context.add_commands(vec![
("format", Arc::new(format)),
("format-list", Arc::new(format_list)),
("ps", Arc::new(ps::ps)), ("ps", Arc::new(ps::ps)),
("ls", Arc::new(ls::ls)), ("ls", Arc::new(ls::ls)),
("cd", Arc::new(cd::cd)), ("cd", Arc::new(cd::cd)),
@ -67,15 +64,18 @@ pub fn cli() -> Result<(), Box<Error>> {
} }
loop { 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) => { LineResult::Success(line) => {
rl.add_history_entry(line.clone()); rl.add_history_entry(line.clone());
} }
LineResult::Error(err) => { LineResult::Error(err) => {
context.host.stdout(&err); context.host.lock().unwrap().stdout(&err);
} }
LineResult::Break => { LineResult::Break => {
@ -85,6 +85,8 @@ pub fn cli() -> Result<(), Box<Error>> {
LineResult::FatalError(err) => { LineResult::FatalError(err) => {
context context
.host .host
.lock()
.unwrap()
.stdout(&format!("A surprising fatal error occurred.\n{:?}", err)); .stdout(&format!("A surprising fatal error occurred.\n{:?}", err));
} }
} }
@ -103,7 +105,7 @@ enum LineResult {
FatalError(ShellError), FatalError(ShellError),
} }
fn process_line(readline: Result<String, ReadlineError>, ctx: &mut Context) -> LineResult { async fn process_line(readline: Result<String, ReadlineError>, ctx: &mut Context) -> LineResult {
match &readline { match &readline {
Ok(line) if line.trim() == "exit" => LineResult::Break, Ok(line) if line.trim() == "exit" => LineResult::Break,
@ -120,29 +122,25 @@ fn process_line(readline: Result<String, ReadlineError>, ctx: &mut Context) -> L
let parsed = result.1; let parsed = result.1;
let mut input = VecDeque::new(); let mut input: InputStream = VecDeque::new().boxed();
for item in parsed { for item in parsed {
input = match process_command(item.clone(), input, ctx) { input = match process_command(item.clone(), input, ctx).await {
Ok(val) => val, Ok(val) => val,
Err(err) => return LineResult::Error(format!("{}", err.description())), Err(err) => return LineResult::Error(format!("{}", err.description())),
}; };
} }
if input.len() > 0 { let input_vec: VecDeque<_> = input.collect().await;
if equal_shapes(&input) {
let array = crate::commands::stream_to_array(input); 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); let args = CommandArgs::from_context(ctx, vec![], array);
match format(args) { format(args).await;
Ok(_) => {}
Err(err) => return LineResult::Error(err.to_string()),
}
} else { } else {
let args = CommandArgs::from_context(ctx, vec![], input); let args = CommandArgs::from_context(ctx, vec![], input_vec.boxed());
match format(args) { format(args).await;
Ok(_) => {}
Err(err) => return LineResult::Error(err.to_string()),
}
} }
} }
@ -163,14 +161,14 @@ fn process_line(readline: Result<String, ReadlineError>, ctx: &mut Context) -> L
} }
} }
fn process_command( async fn process_command(
parsed: Vec<crate::parser::Item>, parsed: Vec<crate::parser::Item>,
input: VecDeque<Value>, input: InputStream,
context: &mut Context, context: &mut Context,
) -> Result<VecDeque<Value>, ShellError> { ) -> Result<InputStream, ShellError> {
let command = classify_command(&parsed, context)?; let command = classify_command(&parsed, context)?;
command.run(input, context) command.run(input, context).await
} }
fn classify_command( fn classify_command(
@ -199,25 +197,26 @@ fn classify_command(
} }
} }
fn format(args: CommandArgs<'caller>) -> Result<VecDeque<ReturnValue>, ShellError> { async fn format(args: CommandArgs) -> OutputStream {
let last = args.input.len() - 1; let input: Vec<_> = args.input.collect().await;
for (i, item) in args.input.iter().enumerate() { let last = input.len() - 1;
for (i, item) in input.iter().enumerate() {
let view = GenericView::new(item); 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 { if last != i {
println!(""); println!("");
} }
} }
Ok(VecDeque::new()) empty_stream()
} }
fn format_list(args: CommandArgs<'caller>) -> Result<VecDeque<ReturnValue>, ShellError> { async fn format_list(args: CommandArgs) -> OutputStream {
let view = EntriesListView::from_stream(args.input); let view = EntriesListView::from_stream(args.input).await;
crate::format::print_view(&view, args.host); crate::format::print_view(&view, &mut *args.host.lock().unwrap());
Ok(VecDeque::new()) empty_stream()
} }
fn equal_shapes(input: &VecDeque<Value>) -> bool { fn equal_shapes(input: &VecDeque<Value>) -> bool {

View file

@ -2,18 +2,18 @@ use crate::errors::ShellError;
use crate::prelude::*; use crate::prelude::*;
use std::env; use std::env;
pub fn cd(args: CommandArgs<'caller>) -> Result<VecDeque<ReturnValue>, ShellError> { pub fn cd(args: CommandArgs) -> Result<OutputStream, ShellError> {
let target = match args.args.first() { let target = match args.args.first() {
// TODO: This needs better infra // TODO: This needs better infra
None => return Err(ShellError::string(format!("cd must take one arg"))), None => return Err(ShellError::string(format!("cd must take one arg"))),
Some(v) => v.as_string()?.clone(), 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 mut stream = VecDeque::new();
let path = dunce::canonicalize(cwd.join(&target).as_path())?; let path = dunce::canonicalize(cwd.join(&target).as_path())?;
let _ = env::set_current_dir(&path); let _ = env::set_current_dir(&path);
stream.push_back(ReturnValue::change_cwd(path)); stream.push_back(ReturnValue::change_cwd(path));
Ok(stream) Ok(stream.boxed())
} }

View file

@ -8,37 +8,38 @@ crate enum ClassifiedCommand {
} }
impl ClassifiedCommand { impl ClassifiedCommand {
crate fn run( crate async fn run(
self, self,
input: VecDeque<Value>, input: InputStream,
context: &mut Context, context: &mut Context,
) -> Result<VecDeque<Value>, ShellError> { ) -> Result<InputStream, ShellError> {
match self { match self {
ClassifiedCommand::Internal(internal) => { ClassifiedCommand::Internal(internal) => {
let result = context.run_command(internal.command, internal.args, input)?; 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 {
for v in result {
match v {
ReturnValue::Action(action) => match action { ReturnValue::Action(action) => match action {
CommandAction::ChangeCwd(cwd) => context.env.cwd = cwd, CommandAction::ChangeCwd(cwd) => {
env.lock().unwrap().cwd = cwd;
futures::future::ready(None)
}
}, },
ReturnValue::Value(v) => next.push_back(v), ReturnValue::Value(v) => futures::future::ready(Some(v)),
} });
}
Ok(next) Ok(stream.boxed() as InputStream)
} }
ClassifiedCommand::External(external) => { ClassifiedCommand::External(external) => {
Exec::shell(&external.name) Exec::shell(&external.name)
.args(&external.args) .args(&external.args)
.cwd(context.env.cwd()) .cwd(context.env.lock().unwrap().cwd())
.join() .join()
.unwrap(); .unwrap();
Ok(VecDeque::new())
Ok(VecDeque::new().boxed() as InputStream)
} }
} }
} }

View file

@ -3,22 +3,22 @@ use crate::object::Value;
use crate::prelude::*; use crate::prelude::*;
use std::path::PathBuf; use std::path::PathBuf;
pub struct CommandArgs<'caller> { pub struct CommandArgs {
pub host: &'caller mut dyn Host, pub host: Arc<Mutex<dyn Host>>,
pub env: &'caller Environment, pub env: Arc<Mutex<Environment>>,
pub args: Vec<Value>, pub args: Vec<Value>,
pub input: VecDeque<Value>, pub input: InputStream,
} }
impl CommandArgs<'caller> { impl CommandArgs {
crate fn from_context( crate fn from_context(
ctx: &'caller mut Context, ctx: &'caller mut Context,
args: Vec<Value>, args: Vec<Value>,
input: VecDeque<Value>, input: InputStream,
) -> CommandArgs<'caller> { ) -> CommandArgs {
CommandArgs { CommandArgs {
host: &mut ctx.host, host: ctx.host.clone(),
env: &ctx.env, env: ctx.env.clone(),
args, args,
input, input,
} }
@ -49,14 +49,14 @@ impl ReturnValue {
} }
pub trait Command { pub trait Command {
fn run(&self, args: CommandArgs<'caller>) -> Result<VecDeque<ReturnValue>, ShellError>; fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError>;
} }
impl<F> Command for F impl<F> Command for F
where where
F: Fn(CommandArgs<'_>) -> Result<VecDeque<ReturnValue>, ShellError>, F: Fn(CommandArgs) -> Result<OutputStream, ShellError>,
{ {
fn run(&self, args: CommandArgs<'caller>) -> Result<VecDeque<ReturnValue>, ShellError> { fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
self(args) self(args)
} }
} }

View file

@ -2,8 +2,8 @@ use crate::errors::ShellError;
use crate::object::{dir_entry_dict, Value}; use crate::object::{dir_entry_dict, Value};
use crate::prelude::*; use crate::prelude::*;
pub fn ls(args: CommandArgs<'value>) -> Result<VecDeque<ReturnValue>, ShellError> { pub fn ls(args: CommandArgs) -> Result<OutputStream, ShellError> {
let cwd = args.env.cwd().to_path_buf(); let cwd = args.env.lock().unwrap().cwd().to_path_buf();
let entries = std::fs::read_dir(&cwd).map_err(|e| ShellError::string(format!("{:?}", e)))?; 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<VecDeque<ReturnValue>, ShellError
shell_entries.push_back(ReturnValue::Value(value)) shell_entries.push_back(ReturnValue::Value(value))
} }
Ok(shell_entries) Ok(shell_entries.boxed())
} }

View file

@ -4,7 +4,7 @@ use crate::object::Value;
use crate::prelude::*; use crate::prelude::*;
use sysinfo::SystemExt; use sysinfo::SystemExt;
pub fn ps(_args: CommandArgs<'caller>) -> Result<VecDeque<ReturnValue>, ShellError> { pub fn ps(_args: CommandArgs) -> Result<OutputStream, ShellError> {
let mut system = sysinfo::System::new(); let mut system = sysinfo::System::new();
system.refresh_all(); system.refresh_all();
@ -15,5 +15,5 @@ pub fn ps(_args: CommandArgs<'caller>) -> Result<VecDeque<ReturnValue>, ShellErr
.map(|(_, process)| ReturnValue::Value(Value::Object(process_dict(process)))) .map(|(_, process)| ReturnValue::Value(Value::Object(process_dict(process))))
.collect::<VecDeque<_>>(); .collect::<VecDeque<_>>();
Ok(list) Ok(list.boxed())
} }

View file

@ -3,7 +3,7 @@ use crate::object::base::reject_fields;
use crate::object::Value; use crate::object::Value;
use crate::prelude::*; use crate::prelude::*;
pub fn reject(args: CommandArgs<'value>) -> Result<VecDeque<ReturnValue>, ShellError> { pub fn reject(args: CommandArgs) -> Result<OutputStream, ShellError> {
if args.args.is_empty() { if args.args.is_empty() {
return Err(ShellError::string("select requires a field")); return Err(ShellError::string("select requires a field"));
} }
@ -11,12 +11,10 @@ pub fn reject(args: CommandArgs<'value>) -> Result<VecDeque<ReturnValue>, ShellE
let fields: Result<Vec<String>, _> = args.args.iter().map(|a| a.as_string()).collect(); let fields: Result<Vec<String>, _> = args.args.iter().map(|a| a.as_string()).collect();
let fields = fields?; let fields = fields?;
let objects = args let stream = args
.input .input
.iter() .map(move |item| Value::Object(reject_fields(&item, &fields)))
.map(|item| Value::Object(reject_fields(item, &fields))) .map(|item| ReturnValue::Value(item));
.map(|item| ReturnValue::Value(item))
.collect();
Ok(objects) Ok(stream.boxed())
} }

View file

@ -3,7 +3,7 @@ use crate::object::base::select_fields;
use crate::object::Value; use crate::object::Value;
use crate::prelude::*; use crate::prelude::*;
pub fn select(args: CommandArgs<'caller>) -> Result<VecDeque<ReturnValue>, ShellError> { pub fn select(args: CommandArgs) -> Result<OutputStream, ShellError> {
if args.args.is_empty() { if args.args.is_empty() {
return Err(ShellError::string("select requires a field")); return Err(ShellError::string("select requires a field"));
} }
@ -13,10 +13,9 @@ pub fn select(args: CommandArgs<'caller>) -> Result<VecDeque<ReturnValue>, Shell
let objects = args let objects = args
.input .input
.iter() .map(move |item| Value::Object(select_fields(&item, &fields)))
.map(|item| Value::Object(select_fields(item, &fields))) .map(|item| ReturnValue::Value(item));
.map(|item| ReturnValue::Value(item))
.collect();
Ok(objects) let stream = Pin::new(Box::new(objects));
Ok(stream)
} }

View file

@ -1,21 +1,13 @@
use crate::errors::ShellError; use crate::errors::ShellError;
use crate::prelude::*; use crate::prelude::*;
pub fn skip(args: CommandArgs<'caller>) -> Result<VecDeque<ReturnValue>, ShellError> { pub fn skip(args: CommandArgs) -> Result<OutputStream, ShellError> {
let amount = args.args[0].as_int()?; let amount = args.args[0].as_int()?;
let amount = if args.input.len() > amount as usize { let input = args.input;
amount as usize
} else {
args.input.len()
};
let out: VecDeque<ReturnValue> = args Ok(input
.input .skip(amount as u64)
.into_iter()
.skip(amount)
.map(|v| ReturnValue::Value(v)) .map(|v| ReturnValue::Value(v))
.collect(); .boxed())
Ok(out)
} }

View file

@ -1,23 +1,26 @@
use crate::errors::ShellError; use crate::errors::ShellError;
use crate::prelude::*; use crate::prelude::*;
use futures::stream::BoxStream;
pub fn sort_by(args: CommandArgs<'caller>) -> Result<VecDeque<ReturnValue>, ShellError> { pub fn sort_by(args: CommandArgs) -> Result<OutputStream, ShellError> {
let fields: Result<Vec<_>, _> = args.args.iter().map(|a| a.as_string()).collect(); let fields: Result<Vec<_>, _> = args.args.iter().map(|a| a.as_string()).collect();
let fields = fields?; let fields = fields?;
let mut output = args.input.into_iter().collect::<Vec<_>>(); let output = args.input.collect::<Vec<_>>();
output.sort_by_key(|item| { let output = output.map(move |mut vec| {
vec.sort_by_key(|item| {
fields fields
.iter() .iter()
.map(|f| item.get_data_by_key(f).borrow().copy()) .map(|f| item.get_data_by_key(f).borrow().copy())
.collect::<Vec<Value>>() .collect::<Vec<Value>>()
}); });
let output = output vec.into_iter()
.iter() .map(|v| ReturnValue::Value(v.copy()))
.map(|o| ReturnValue::Value(o.copy())) .collect::<VecDeque<_>>()
.collect(); .boxed()
});
Ok(output) Ok(output.flatten_stream().boxed())
} }

View file

@ -3,21 +3,13 @@ use crate::prelude::*;
// TODO: "Amount remaining" wrapper // TODO: "Amount remaining" wrapper
pub fn take(args: CommandArgs<'caller>) -> Result<VecDeque<ReturnValue>, ShellError> { pub fn take(args: CommandArgs) -> Result<OutputStream, ShellError> {
let amount = args.args[0].as_int()?; let amount = args.args[0].as_int()?;
let amount = if args.input.len() > amount as usize { let input = args.input;
amount as usize
} else {
args.input.len()
};
let out: VecDeque<ReturnValue> = args Ok(input
.input .take(amount as u64)
.into_iter()
.take(amount)
.map(|v| ReturnValue::Value(v)) .map(|v| ReturnValue::Value(v))
.collect(); .boxed())
Ok(out)
} }

View file

@ -1,15 +1,17 @@
use crate::errors::ShellError;
use crate::object::Value; use crate::object::Value;
use crate::prelude::*; use crate::prelude::*;
pub fn to_array(args: CommandArgs<'caller>) -> Result<VecDeque<ReturnValue>, ShellError> { pub fn to_array(args: CommandArgs) -> Result<OutputStream, ShellError> {
let out = args.input.into_iter().collect(); let out = args.input.collect();
Ok(ReturnValue::single(Value::List(out))) Ok(out
.map(|vec: Vec<_>| single_output(Value::List(vec)))
.flatten_stream()
.boxed())
} }
crate fn stream_to_array(stream: VecDeque<Value>) -> VecDeque<Value> { crate async fn stream_to_array(stream: InputStream) -> InputStream {
let out = Value::List(stream.into_iter().collect()); let out = Value::List(stream.collect().await);
let mut stream = VecDeque::new(); let mut stream = VecDeque::new();
stream.push_back(out); stream.push_back(out);
stream stream.boxed() as InputStream
} }

View file

@ -2,14 +2,14 @@ use crate::errors::ShellError;
use crate::prelude::*; use crate::prelude::*;
use prettyprint::PrettyPrinter; use prettyprint::PrettyPrinter;
pub fn view(args: CommandArgs<'caller>) -> Result<VecDeque<ReturnValue>, ShellError> { pub fn view(args: CommandArgs) -> Result<OutputStream, ShellError> {
let target = match args.args.first() { let target = match args.args.first() {
// TODO: This needs better infra // TODO: This needs better infra
None => return Err(ShellError::string(format!("cat must take one arg"))), None => return Err(ShellError::string(format!("cat must take one arg"))),
Some(v) => v.as_string()?.clone(), 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() let printer = PrettyPrinter::default()
.line_numbers(false) .line_numbers(false)
@ -22,5 +22,5 @@ pub fn view(args: CommandArgs<'caller>) -> Result<VecDeque<ReturnValue>, ShellEr
let _ = printer.file(file.display().to_string()); let _ = printer.file(file.display().to_string());
Ok(VecDeque::new()) Ok(VecDeque::new().boxed())
} }

View file

@ -2,26 +2,27 @@ use crate::errors::ShellError;
use crate::object::base::find; use crate::object::base::find;
use crate::prelude::*; use crate::prelude::*;
pub fn r#where(args: CommandArgs<'caller>) -> Result<VecDeque<ReturnValue>, ShellError> { pub fn r#where(args: CommandArgs) -> Result<OutputStream, ShellError> {
if args.args.is_empty() { if args.args.is_empty() {
return Err(ShellError::string("select requires a field")); return Err(ShellError::string("select requires a field"));
} }
let field: Result<String, _> = args.args[0].as_string(); let field: Result<String, _> = args.args[0].as_string();
let field = field?; let field = field?;
let input = args.input;
let operator = args.args[1].copy();
match args.args[1] { match operator {
Value::Primitive(Primitive::Operator(ref operator)) => { Value::Primitive(Primitive::Operator(operator)) => {
let objects = args let right = args.args[2].copy();
.input
.iter()
.filter(|item| find(&item, &field, operator, &args.args[2]))
.map(|item| ReturnValue::Value(item.copy()))
.collect();
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); println!("{:?}", x);
Err(ShellError::string("expected a comparison operator")) Err(ShellError::string("expected a comparison operator"))
} }

View file

@ -5,16 +5,16 @@ use std::sync::Arc;
pub struct Context { pub struct Context {
commands: indexmap::IndexMap<String, Arc<dyn Command>>, commands: indexmap::IndexMap<String, Arc<dyn Command>>,
crate host: Box<dyn Host>, crate host: Arc<Mutex<dyn Host>>,
crate env: Environment, crate env: Arc<Mutex<Environment>>,
} }
impl Context { impl Context {
crate fn basic() -> Result<Context, Box<Error>> { crate fn basic() -> Result<Context, Box<Error>> {
Ok(Context { Ok(Context {
commands: indexmap::IndexMap::new(), commands: indexmap::IndexMap::new(),
host: Box::new(crate::env::host::BasicHost), host: Arc::new(Mutex::new(crate::env::host::BasicHost)),
env: Environment::basic()?, env: Arc::new(Mutex::new(Environment::basic()?)),
}) })
} }
@ -36,11 +36,11 @@ impl Context {
&mut self, &mut self,
command: Arc<dyn Command>, command: Arc<dyn Command>,
arg_list: Vec<Value>, arg_list: Vec<Value>,
input: VecDeque<Value>, input: InputStream,
) -> Result<VecDeque<ReturnValue>, ShellError> { ) -> Result<OutputStream, ShellError> {
let command_args = CommandArgs { let command_args = CommandArgs {
host: &mut self.host, host: self.host.clone(),
env: &self.env, env: self.env.clone(),
args: arg_list, args: arg_list,
input, input,
}; };

View file

@ -50,8 +50,10 @@ pub struct EntriesListView {
} }
impl EntriesListView { impl EntriesListView {
crate fn from_stream(values: VecDeque<Value>) -> EntriesListView { crate async fn from_stream(values: InputStream) -> EntriesListView {
EntriesListView { values } EntriesListView {
values: values.collect().await,
}
} }
} }

View file

@ -1,5 +1,6 @@
#![feature(crate_visibility_modifier)] #![feature(crate_visibility_modifier)]
#![feature(in_band_lifetimes)] #![feature(in_band_lifetimes)]
#![feature(async_await)]
mod cli; mod cli;
mod commands; mod commands;
@ -16,5 +17,6 @@ mod stream;
use std::error::Error; use std::error::Error;
fn main() -> Result<(), Box<Error>> { fn main() -> Result<(), Box<Error>> {
crate::cli::cli() futures::executor::block_on(crate::cli::cli());
Ok(())
} }

View file

@ -17,7 +17,7 @@ pub enum Item {
Operator(Operator), Operator(Operator),
} }
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum Operator { pub enum Operator {
Equal, Equal,
NotEqual, NotEqual,

View file

@ -4,4 +4,10 @@ crate 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::object::{Primitive, Value}; 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::collections::VecDeque;
crate use std::pin::Pin;
crate use std::sync::{Arc, Mutex};

View file

@ -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()
}