mirror of
https://github.com/nushell/nushell
synced 2024-12-26 04:53:09 +00:00
Data flows across commands via streams now
This commit is contained in:
parent
31dd579d6f
commit
625a356361
21 changed files with 171 additions and 157 deletions
1
rustfmt.toml
Normal file
1
rustfmt.toml
Normal file
|
@ -0,0 +1 @@
|
||||||
|
edition = "2018"
|
71
src/cli.rs
71
src/cli.rs
|
@ -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 {
|
||||||
|
|
|
@ -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())
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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())
|
||||||
}
|
}
|
||||||
|
|
|
@ -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())
|
||||||
}
|
}
|
||||||
|
|
|
@ -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())
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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())
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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())
|
||||||
}
|
}
|
||||||
|
|
|
@ -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"))
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
||||||
};
|
};
|
||||||
|
|
|
@ -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,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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};
|
||||||
|
|
|
@ -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()
|
||||||
|
}
|
Loading…
Reference in a new issue