From 5a8e041a48af4b8f57f6035d7ec44f7ab85ba8a4 Mon Sep 17 00:00:00 2001 From: Yehuda Katz Date: Tue, 23 Jul 2019 15:22:11 -0700 Subject: [PATCH 01/12] Tests pass! --- .gitignore | 1 + src/cli.rs | 41 ++-- src/commands.rs | 6 +- src/commands/cd.rs | 8 +- src/commands/classified.rs | 17 +- src/commands/clip.rs | 2 +- src/commands/command.rs | 286 ++++++++++++++++++++++-- src/commands/config.rs | 24 +- src/commands/exit.rs | 2 +- src/commands/first.rs | 13 +- src/commands/from_csv.rs | 6 +- src/commands/from_ini.rs | 6 +- src/commands/from_json.rs | 9 +- src/commands/from_toml.rs | 9 +- src/commands/from_xml.rs | 5 +- src/commands/from_yaml.rs | 8 +- src/commands/get.rs | 27 +-- src/commands/lines.rs | 7 +- src/commands/ls.rs | 10 +- src/commands/macros.rs | 5 +- src/commands/open.rs | 137 +++++++++--- src/commands/pick.rs | 21 +- src/commands/plugin.rs | 18 +- src/commands/ps.rs | 4 +- src/commands/reject.rs | 22 +- src/commands/rm.rs | 19 +- src/commands/save.rs | 2 +- src/commands/size.rs | 2 +- src/commands/skip_while.rs | 26 ++- src/commands/sort_by.rs | 15 +- src/commands/split_column.rs | 16 +- src/commands/split_row.rs | 19 +- src/commands/sysinfo.rs | 4 +- src/commands/to_array.rs | 2 +- src/commands/to_csv.rs | 43 ++-- src/commands/to_json.rs | 6 +- src/commands/to_toml.rs | 5 +- src/commands/to_yaml.rs | 5 +- src/commands/trim.rs | 2 +- src/commands/where_.rs | 79 +++++-- src/context.rs | 122 +++++++--- src/env/host.rs | 2 +- src/evaluate/evaluator.rs | 14 +- src/lib.rs | 3 +- src/object/base.rs | 8 +- src/parser/hir.rs | 112 +++++++++- src/parser/hir/baseline_parse_tokens.rs | 10 +- src/parser/hir/binary.rs | 17 +- src/parser/hir/named.rs | 22 +- src/parser/hir/path.rs | 19 +- src/parser/parse/operator.rs | 8 + src/parser/parse/span.rs | 7 + src/parser/parse/text.rs | 19 ++ src/parser/parse_command.rs | 4 +- src/parser/registry.rs | 140 +++--------- src/prelude.rs | 9 +- src/shell/completer.rs | 6 +- src/shell/helper.rs | 2 +- src/traits.rs | 28 +++ tests/fixtures/nuplayground/.gitignore | 2 - tests/helpers/mod.rs | 20 +- 61 files changed, 1111 insertions(+), 402 deletions(-) create mode 100644 src/traits.rs delete mode 100644 tests/fixtures/nuplayground/.gitignore diff --git a/.gitignore b/.gitignore index 5a7ebb7172..6d330b7f8d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ /target **/*.rs.bk history.txt +tests/fixtures/nuplayground \ No newline at end of file diff --git a/src/cli.rs b/src/cli.rs index 15b6a50b85..a568b9e29f 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -9,13 +9,11 @@ use crate::commands::plugin::JsonRpc; use crate::commands::plugin::{PluginCommand, PluginSink}; use crate::context::Context; crate use crate::errors::ShellError; -use crate::evaluate::Scope; use crate::git::current_branch; use crate::object::Value; use crate::parser::parse::span::Spanned; -use crate::parser::registry; use crate::parser::registry::CommandConfig; -use crate::parser::{Pipeline, PipelineElement, TokenNode}; +use crate::parser::{hir, Pipeline, PipelineElement, TokenNode}; use crate::prelude::*; use log::{debug, trace}; @@ -150,22 +148,22 @@ pub async fn cli() -> Result<(), Box> { use crate::commands::*; context.add_commands(vec![ + command("first", Box::new(first::first)), + command("pick", Box::new(pick::pick)), + command("from-ini", Box::new(from_ini::from_ini)), + command("from-csv", Box::new(from_csv::from_csv)), + command("from-json", Box::new(from_json::from_json)), + command("from-toml", Box::new(from_toml::from_toml)), + command("from-xml", Box::new(from_xml::from_xml)), command("ps", Box::new(ps::ps)), command("ls", Box::new(ls::ls)), command("sysinfo", Box::new(sysinfo::sysinfo)), command("cd", Box::new(cd::cd)), - command("first", Box::new(first::first)), command("size", Box::new(size::size)), - command("from-csv", Box::new(from_csv::from_csv)), - command("from-ini", Box::new(from_ini::from_ini)), - command("from-json", Box::new(from_json::from_json)), - command("from-toml", Box::new(from_toml::from_toml)), - command("from-xml", Box::new(from_xml::from_xml)), command("from-yaml", Box::new(from_yaml::from_yaml)), command("get", Box::new(get::get)), command("exit", Box::new(exit::exit)), command("lines", Box::new(lines::lines)), - command("pick", Box::new(pick::pick)), command("split-column", Box::new(split_column::split_column)), command("split-row", Box::new(split_row::split_row)), command("lines", Box::new(lines::lines)), @@ -348,10 +346,11 @@ async fn process_line(readline: Result, ctx: &mut Context _ => pipeline.commands.push(ClassifiedCommand::Sink(SinkCommand { command: sink("autoview", Box::new(autoview::autoview)), name_span: None, - args: registry::Args { - positional: None, - named: None, - }, + args: hir::Call::new( + Box::new(hir::Expression::synthetic_string("autoview")), + None, + None, + ), })), } @@ -386,7 +385,7 @@ async fn process_line(readline: Result, ctx: &mut Context (Some(ClassifiedCommand::Sink(left)), None) => { let input_vec: Vec> = input.objects.into_vec().await; - if let Err(err) = left.run(ctx, input_vec) { + if let Err(err) = left.run(ctx, input_vec, &Text::from(line)) { return LineResult::Error(line.clone(), err); } break; @@ -395,20 +394,20 @@ async fn process_line(readline: Result, ctx: &mut Context ( Some(ClassifiedCommand::Internal(left)), Some(ClassifiedCommand::External(_)), - ) => match left.run(ctx, input).await { + ) => match left.run(ctx, input, Text::from(line)).await { Ok(val) => ClassifiedInputStream::from_input_stream(val), Err(err) => return LineResult::Error(line.clone(), err), }, (Some(ClassifiedCommand::Internal(left)), Some(_)) => { - match left.run(ctx, input).await { + match left.run(ctx, input, Text::from(line)).await { Ok(val) => ClassifiedInputStream::from_input_stream(val), Err(err) => return LineResult::Error(line.clone(), err), } } (Some(ClassifiedCommand::Internal(left)), None) => { - match left.run(ctx, input).await { + match left.run(ctx, input, Text::from(line)).await { Ok(val) => ClassifiedInputStream::from_input_stream(val), Err(err) => return LineResult::Error(line.clone(), err), } @@ -487,11 +486,10 @@ fn classify_command( true => { let command = context.get_command(name); let config = command.config(); - let scope = Scope::empty(); trace!(target: "nu::build_pipeline", "classifying {:?}", config); - let args = config.evaluate_args(call, context, &scope, source)?; + let args: hir::Call = config.parse_args(call, context.registry(), source)?; Ok(ClassifiedCommand::Internal(InternalCommand { command, @@ -504,9 +502,8 @@ fn classify_command( true => { let command = context.get_sink(name); let config = command.config(); - let scope = Scope::empty(); - let args = config.evaluate_args(call, context, &scope, source)?; + let args = config.parse_args(call, context.registry(), source)?; Ok(ClassifiedCommand::Sink(SinkCommand { command, diff --git a/src/commands.rs b/src/commands.rs index e1ba9b7935..8d813191ce 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -4,7 +4,6 @@ crate mod macros; crate mod args; crate mod autoview; crate mod cd; -crate mod rm; crate mod classified; crate mod clip; crate mod command; @@ -25,6 +24,7 @@ crate mod pick; crate mod plugin; crate mod ps; crate mod reject; +crate mod rm; crate mod save; crate mod size; crate mod skip_while; @@ -42,9 +42,9 @@ crate mod trim; crate mod vtable; crate mod where_; -crate use command::command; +crate use command::{command, filter, EvaluatedFilterCommandArgs, EvaluatedStaticCommandArgs}; crate use config::Config; -crate use rm::Remove; crate use open::Open; +crate use rm::Remove; crate use skip_while::SkipWhile; crate use where_::Where; diff --git a/src/commands/cd.rs b/src/commands/cd.rs index b68a0deb70..06fd327fa5 100644 --- a/src/commands/cd.rs +++ b/src/commands/cd.rs @@ -2,8 +2,10 @@ use crate::errors::ShellError; use crate::prelude::*; use std::env; -pub fn cd(args: CommandArgs) -> Result { - let env = args.env.lock().unwrap(); +pub fn cd(args: CommandArgs, registry: &CommandRegistry) -> Result { + let env = args.env.clone(); + let env = env.lock().unwrap(); + let args = args.evaluate_once(registry)?; let cwd = env.path().to_path_buf(); let path = match args.nth(0) { @@ -13,7 +15,7 @@ pub fn cd(args: CommandArgs) -> Result { return Err(ShellError::maybe_labeled_error( "Can not change to home directory", "can not go to home", - args.call_info.name_span, + args.name_span(), )) } }, diff --git a/src/commands/classified.rs b/src/commands/classified.rs index da4ce706cc..9abb7570eb 100644 --- a/src/commands/classified.rs +++ b/src/commands/classified.rs @@ -1,6 +1,7 @@ use crate::commands::command::Sink; use crate::context::SourceMap; -use crate::parser::{registry::Args, Span, Spanned, TokenNode}; +use crate::evaluate::Scope; +use crate::parser::{hir, Span, Spanned, TokenNode}; use crate::prelude::*; use bytes::{BufMut, BytesMut}; use futures::stream::StreamExt; @@ -101,7 +102,7 @@ impl ClassifiedCommand { crate struct SinkCommand { crate command: Arc, crate name_span: Option, - crate args: Args, + crate args: hir::Call, } impl SinkCommand { @@ -109,8 +110,12 @@ impl SinkCommand { self, context: &mut Context, input: Vec>, + source: &Text, ) -> Result<(), ShellError> { - context.run_sink(self.command, self.name_span.clone(), self.args, input) + let args = self + .args + .evaluate(context.registry(), &Scope::empty(), source)?; + context.run_sink(self.command, self.name_span.clone(), args, input) } } @@ -118,7 +123,7 @@ crate struct InternalCommand { crate command: Arc, crate name_span: Option, crate source_map: SourceMap, - crate args: Args, + crate args: hir::Call, } impl InternalCommand { @@ -126,11 +131,12 @@ impl InternalCommand { self, context: &mut Context, input: ClassifiedInputStream, + source: Text, ) -> Result { if log_enabled!(log::Level::Trace) { trace!(target: "nu::run::internal", "->"); trace!(target: "nu::run::internal", "{}", self.command.name()); - trace!(target: "nu::run::internal", "{:?}", self.args.debug()); + trace!(target: "nu::run::internal", "{}", self.args.debug(&source)); } let objects: InputStream = @@ -141,6 +147,7 @@ impl InternalCommand { self.name_span.clone(), self.source_map, self.args, + source, objects, )?; diff --git a/src/commands/clip.rs b/src/commands/clip.rs index 32294bf776..c2527234d9 100644 --- a/src/commands/clip.rs +++ b/src/commands/clip.rs @@ -16,7 +16,7 @@ pub fn clip(args: SinkCommandArgs) -> Result<(), ShellError> { } let string = i.as_string().map_err(labelled( - args.call_info.name_span, + args.name_span(), "Given non-string data", "expected strings from pipeline", ))?; diff --git a/src/commands/command.rs b/src/commands/command.rs index e5057c6cd1..1e14fbfcea 100644 --- a/src/commands/command.rs +++ b/src/commands/command.rs @@ -1,20 +1,62 @@ -use crate::context::SourceMap; -use crate::context::SpanSource; +use crate::context::{SourceMap, SpanSource}; use crate::errors::ShellError; +use crate::evaluate::Scope; use crate::object::Value; -use crate::parser::{ - registry::{self, Args}, - Span, Spanned, -}; +use crate::parser::hir; +use crate::parser::{registry, Span, Spanned}; use crate::prelude::*; use getset::Getters; use serde::{Deserialize, Serialize}; +use std::fmt; +use std::ops::Deref; use std::path::PathBuf; use uuid::Uuid; +#[derive(Deserialize, Serialize, Debug, Clone)] +pub struct UnevaluatedCallInfo { + pub args: hir::Call, + pub source: Text, + pub source_map: SourceMap, + pub name_span: Option, +} + +impl ToDebug for UnevaluatedCallInfo { + fn fmt_debug(&self, f: &mut fmt::Formatter, source: &str) -> fmt::Result { + self.args.fmt_debug(f, source) + } +} + +impl UnevaluatedCallInfo { + fn name(&self) -> Result<&str, ShellError> { + let head = &self.args.head(); + match head.item() { + hir::RawExpression::Literal(hir::Literal::Bare) => Ok(head.span.slice(&self.source)), + hir::RawExpression::Literal(hir::Literal::String(span)) => Ok(span.slice(&self.source)), + other => Err(ShellError::type_error( + "Command name", + head.type_name().spanned(head.span), + )), + } + } + + fn evaluate( + self, + registry: ®istry::CommandRegistry, + scope: &Scope, + ) -> Result { + let args = self.args.evaluate(registry, scope, &self.source)?; + + Ok(CallInfo { + args, + source_map: self.source_map, + name_span: self.name_span, + }) + } +} + #[derive(Deserialize, Serialize, Debug)] pub struct CallInfo { - pub args: Args, + pub args: registry::EvaluatedArgs, pub source_map: SourceMap, pub name_span: Option, } @@ -22,13 +64,137 @@ pub struct CallInfo { #[derive(Getters)] #[get = "crate"] pub struct CommandArgs { - pub host: Arc>, + pub host: Arc>, pub env: Arc>, - pub call_info: CallInfo, + pub call_info: UnevaluatedCallInfo, pub input: InputStream, } +impl ToDebug for CommandArgs { + fn fmt_debug(&self, f: &mut fmt::Formatter, source: &str) -> fmt::Result { + self.call_info.fmt_debug(f, source) + } +} + impl CommandArgs { + pub fn evaluate_once( + self, + registry: ®istry::CommandRegistry, + ) -> Result { + let host = self.host.clone(); + let env = self.env.clone(); + let input = self.input; + let call_info = self.call_info.evaluate(registry, &Scope::empty())?; + + Ok(EvaluatedStaticCommandArgs::new(host, env, call_info, input)) + } + + pub fn name_span(&self) -> Option { + self.call_info.name_span + } +} + +pub enum EvaluatedInput { + Static(InputStream), + Filter(Spanned), +} + +impl EvaluatedInput { + pub fn stream(self) -> InputStream { + match self { + EvaluatedInput::Static(stream) => stream, + EvaluatedInput::Filter(value) => vec![value].into(), + } + } +} + +pub struct EvaluatedStaticCommandArgs { + pub args: EvaluatedCommandArgs, + pub input: InputStream, +} + +impl Deref for EvaluatedStaticCommandArgs { + type Target = EvaluatedCommandArgs; + fn deref(&self) -> &Self::Target { + &self.args + } +} + +impl EvaluatedStaticCommandArgs { + pub fn new( + host: Arc>, + env: Arc>, + call_info: CallInfo, + input: impl Into, + ) -> EvaluatedStaticCommandArgs { + EvaluatedStaticCommandArgs { + args: EvaluatedCommandArgs { + host, + env, + call_info, + }, + input: input.into(), + } + } + + pub fn name_span(&self) -> Option { + self.args.call_info.name_span + } + + pub fn parts(self) -> (InputStream, registry::EvaluatedArgs) { + let EvaluatedStaticCommandArgs { args, input } = self; + + (input, args.call_info.args) + } +} + +#[derive(Getters)] +#[get = "pub"] +pub struct EvaluatedFilterCommandArgs { + args: EvaluatedCommandArgs, + input: Spanned, +} + +impl Deref for EvaluatedFilterCommandArgs { + type Target = EvaluatedCommandArgs; + fn deref(&self) -> &Self::Target { + &self.args + } +} + +impl EvaluatedFilterCommandArgs { + pub fn new( + host: Arc>, + env: Arc>, + call_info: CallInfo, + input: Spanned, + ) -> EvaluatedFilterCommandArgs { + EvaluatedFilterCommandArgs { + args: EvaluatedCommandArgs { + host, + env, + call_info, + }, + input, + } + } +} + +#[derive(Getters)] +#[get = "crate"] +pub struct EvaluatedCommandArgs { + pub host: Arc>, + pub env: Arc>, + pub call_info: CallInfo, +} + +impl EvaluatedCommandArgs { + pub fn parts(self) -> () {} + + pub fn call_args(&self) -> ®istry::EvaluatedArgs { + &self.call_info.args + } + pub fn nth(&self, pos: usize) -> Option<&Spanned> { self.call_info.args.nth(pos) } @@ -61,6 +227,12 @@ pub struct SinkCommandArgs { pub input: Vec>, } +impl SinkCommandArgs { + pub fn name_span(&self) -> Option { + self.call_info.name_span + } +} + #[derive(Debug, Serialize, Deserialize)] pub enum CommandAction { ChangePath(PathBuf), @@ -100,8 +272,12 @@ impl ReturnSuccess { } } -pub trait Command { - fn run(&self, args: CommandArgs) -> Result; +pub trait Command: Send + Sync { + fn run( + &self, + args: CommandArgs, + registry: ®istry::CommandRegistry, + ) -> Result; fn name(&self) -> &str; fn config(&self) -> registry::CommandConfig { @@ -132,14 +308,74 @@ pub trait Sink { } } -pub struct FnCommand { +pub struct FnFilterCommand { name: String, - func: Box Result>, + func: fn(EvaluatedFilterCommandArgs) -> Result, } -impl Command for FnCommand { - fn run(&self, args: CommandArgs) -> Result { - (self.func)(args) +impl Command for FnFilterCommand { + fn name(&self) -> &str { + &self.name + } + + fn run( + &self, + args: CommandArgs, + registry: ®istry::CommandRegistry, + ) -> Result { + let CommandArgs { + host, + env, + call_info, + input, + } = args; + + let host: Arc> = host.clone(); + let env: Arc> = env.clone(); + let registry: registry::CommandRegistry = registry.clone(); + let func = self.func; + + let result = input.values.map(move |it| { + let registry = registry.clone(); + let call_info = match call_info + .clone() + .evaluate(®istry, &Scope::it_value(it.clone())) + { + Err(err) => return OutputStream::from(vec![Err(err)]).values, + Ok(args) => args, + }; + + let args = EvaluatedFilterCommandArgs::new(host.clone(), env.clone(), call_info, it); + + match func(args) { + Err(err) => return OutputStream::from(vec![Err(err)]).values, + Ok(stream) => stream.values, + } + }); + + let result = result.flatten(); + let result: BoxStream = result.boxed(); + + Ok(result.into()) + } +} + +pub struct FnRawCommand { + name: String, + func: Box< + dyn Fn(CommandArgs, ®istry::CommandRegistry) -> Result + + Send + + Sync, + >, +} + +impl Command for FnRawCommand { + fn run( + &self, + args: CommandArgs, + registry: ®istry::CommandRegistry, + ) -> Result { + (self.func)(args, registry) } fn name(&self) -> &str { @@ -149,9 +385,23 @@ impl Command for FnCommand { pub fn command( name: &str, - func: Box Result>, + func: Box< + dyn Fn(CommandArgs, ®istry::CommandRegistry) -> Result + + Send + + Sync, + >, ) -> Arc { - Arc::new(FnCommand { + Arc::new(FnRawCommand { + name: name.to_string(), + func, + }) +} + +pub fn filter( + name: &str, + func: fn(EvaluatedFilterCommandArgs) -> Result, +) -> Arc { + Arc::new(FnFilterCommand { name: name.to_string(), func, }) diff --git a/src/commands/config.rs b/src/commands/config.rs index 7da3c91c66..6daf062098 100644 --- a/src/commands/config.rs +++ b/src/commands/config.rs @@ -1,10 +1,10 @@ use crate::prelude::*; +use crate::commands::EvaluatedStaticCommandArgs; use crate::errors::ShellError; -use crate::object::config; -use crate::object::Value; +use crate::object::{config, Value}; use crate::parser::hir::SyntaxType; -use crate::parser::registry::{CommandConfig, NamedType}; +use crate::parser::registry::{self, CommandConfig, NamedType}; use indexmap::IndexMap; use log::trace; use std::iter::FromIterator; @@ -12,9 +12,15 @@ use std::iter::FromIterator; pub struct Config; impl Command for Config { - fn run(&self, args: CommandArgs) -> Result { + fn run( + &self, + args: CommandArgs, + registry: ®istry::CommandRegistry, + ) -> Result { + let args = args.evaluate_once(registry)?; config(args) } + fn name(&self) -> &str { "config" } @@ -38,11 +44,11 @@ impl Command for Config { } } -pub fn config(args: CommandArgs) -> Result { - let mut result = crate::object::config::config(args.call_info.name_span)?; +pub fn config(args: EvaluatedStaticCommandArgs) -> Result { + let mut result = crate::object::config::config(args.name_span())?; - trace!("{:#?}", args.call_info.args.positional); - trace!("{:#?}", args.call_info.args.named); + trace!("{:#?}", args.call_args().positional); + trace!("{:#?}", args.call_args().named); if let Some(v) = args.get("get") { let key = v.as_string()?; @@ -95,7 +101,7 @@ pub fn config(args: CommandArgs) -> Result { } if args.len() == 0 { - return Ok(vec![Value::Object(result.into()).spanned(args.call_info.name_span)].into()); + return Ok(vec![Value::Object(result.into()).spanned(args.name_span())].into()); } Err(ShellError::string(format!("Unimplemented"))) diff --git a/src/commands/exit.rs b/src/commands/exit.rs index 64cddfcfb9..8bba11b49b 100644 --- a/src/commands/exit.rs +++ b/src/commands/exit.rs @@ -2,6 +2,6 @@ use crate::commands::command::CommandAction; use crate::errors::ShellError; use crate::prelude::*; -pub fn exit(_args: CommandArgs) -> Result { +pub fn exit(_args: CommandArgs, registry: &CommandRegistry) -> Result { Ok(vec![Ok(ReturnSuccess::Action(CommandAction::Exit))].into()) } diff --git a/src/commands/first.rs b/src/commands/first.rs index aec66b3a62..83745eddb3 100644 --- a/src/commands/first.rs +++ b/src/commands/first.rs @@ -1,14 +1,17 @@ use crate::errors::ShellError; +use crate::parser::CommandRegistry; use crate::prelude::*; // TODO: "Amount remaining" wrapper -pub fn first(args: CommandArgs) -> Result { +pub fn first(args: CommandArgs, registry: &CommandRegistry) -> Result { + let args = args.evaluate_once(registry)?; + if args.len() == 0 { return Err(ShellError::maybe_labeled_error( "First requires an amount", "needs parameter", - args.call_info.name_span, + args.name_span(), )); } @@ -25,7 +28,7 @@ pub fn first(args: CommandArgs) -> Result { } }; - let input = args.input; - - Ok(OutputStream::from_input(input.values.take(amount as u64))) + Ok(OutputStream::from_input( + args.input.values.take(amount as u64), + )) } diff --git a/src/commands/from_csv.rs b/src/commands/from_csv.rs index 22381a35f5..bc65ce29da 100644 --- a/src/commands/from_csv.rs +++ b/src/commands/from_csv.rs @@ -6,7 +6,6 @@ pub fn from_csv_string_to_value( s: String, span: impl Into, ) -> Result, Box> { - let mut reader = ReaderBuilder::new() .has_headers(false) .from_reader(s.as_bytes()); @@ -49,9 +48,10 @@ pub fn from_csv_string_to_value( }) } -pub fn from_csv(args: CommandArgs) -> Result { +pub fn from_csv(args: CommandArgs, registry: &CommandRegistry) -> Result { + let args = args.evaluate_once(registry)?; + let span = args.name_span(); let out = args.input; - let span = args.call_info.name_span; Ok(out .values diff --git a/src/commands/from_ini.rs b/src/commands/from_ini.rs index 193356dce8..6f9a3c0115 100644 --- a/src/commands/from_ini.rs +++ b/src/commands/from_ini.rs @@ -37,9 +37,11 @@ pub fn from_ini_string_to_value( Ok(convert_ini_top_to_nu_value(&v, span)) } -pub fn from_ini(args: CommandArgs) -> Result { +pub fn from_ini(args: CommandArgs, registry: &CommandRegistry) -> Result { + let args = args.evaluate_once(registry)?; + let span = args.name_span(); let out = args.input; - let span = args.call_info.name_span; + Ok(out .values .map(move |a| match a.item { diff --git a/src/commands/from_json.rs b/src/commands/from_json.rs index f3968dd120..110a62eb88 100644 --- a/src/commands/from_json.rs +++ b/src/commands/from_json.rs @@ -43,9 +43,14 @@ pub fn from_json_string_to_value( Ok(convert_json_value_to_nu_value(&v, span)) } -pub fn from_json(args: CommandArgs) -> Result { +pub fn from_json( + args: CommandArgs, + registry: &CommandRegistry, +) -> Result { + let args = args.evaluate_once(registry)?; + let span = args.name_span(); let out = args.input; - let span = args.call_info.name_span; + Ok(out .values .map(move |a| match a.item { diff --git a/src/commands/from_toml.rs b/src/commands/from_toml.rs index cf650dd99f..905d904773 100644 --- a/src/commands/from_toml.rs +++ b/src/commands/from_toml.rs @@ -41,9 +41,14 @@ pub fn from_toml_string_to_value( Ok(convert_toml_value_to_nu_value(&v, span)) } -pub fn from_toml(args: CommandArgs) -> Result { +pub fn from_toml( + args: CommandArgs, + registry: &CommandRegistry, +) -> Result { + let args = args.evaluate_once(registry)?; + let span = args.name_span(); let out = args.input; - let span = args.call_info.name_span; + Ok(out .values .map(move |a| match a.item { diff --git a/src/commands/from_xml.rs b/src/commands/from_xml.rs index cc221df33e..2aec6ac0be 100644 --- a/src/commands/from_xml.rs +++ b/src/commands/from_xml.rs @@ -59,9 +59,10 @@ pub fn from_xml_string_to_value( Ok(from_document_to_value(&parsed, span)) } -pub fn from_xml(args: CommandArgs) -> Result { +pub fn from_xml(args: CommandArgs, registry: &CommandRegistry) -> Result { + let args = args.evaluate_once(registry)?; + let span = args.name_span(); let out = args.input; - let span = args.call_info.name_span; Ok(out .values .map(move |a| match a.item { diff --git a/src/commands/from_yaml.rs b/src/commands/from_yaml.rs index 475926d4ab..69d42697df 100644 --- a/src/commands/from_yaml.rs +++ b/src/commands/from_yaml.rs @@ -48,9 +48,13 @@ pub fn from_yaml_string_to_value( Ok(convert_yaml_value_to_nu_value(&v, span)) } -pub fn from_yaml(args: CommandArgs) -> Result { +pub fn from_yaml( + args: CommandArgs, + registry: &CommandRegistry, +) -> Result { + let span = args.name_span(); let out = args.input; - let span = args.call_info.name_span; + Ok(out .values .map(move |a| match a.item { diff --git a/src/commands/get.rs b/src/commands/get.rs index 6cdea2e1fb..ed5d656354 100644 --- a/src/commands/get.rs +++ b/src/commands/get.rs @@ -21,36 +21,37 @@ fn get_member(path: &str, span: Span, obj: &Spanned) -> Result Result { - if args.len() == 0 { +pub fn get(args: CommandArgs, registry: &CommandRegistry) -> Result { + let args = args.evaluate_once(registry)?; + let span = args.name_span(); + let len = args.len(); + + if len == 0 { return Err(ShellError::maybe_labeled_error( "Get requires a field or field path", "needs parameter", - args.call_info.name_span, + span, )); } let amount = args.expect_nth(0)?.as_i64(); + let (input, args) = args.parts(); + let positional = args.positional; // If it's a number, get the row instead of the column if let Ok(amount) = amount { - return Ok(args - .input - .values - .skip(amount as u64) - .take(1) - .from_input_stream()); + return Ok(input.values.skip(amount as u64).take(1).from_input_stream()); } - let fields: Result, _> = args - .positional_iter() + let fields: Result, _> = positional + .iter() + .flatten() .map(|a| (a.as_string().map(|x| (x, a.span)))) .collect(); let fields = fields?; - let stream = args - .input + let stream = input .values .map(move |item| { let mut result = VecDeque::new(); diff --git a/src/commands/lines.rs b/src/commands/lines.rs index 219f4d39db..e1c46acff3 100644 --- a/src/commands/lines.rs +++ b/src/commands/lines.rs @@ -5,9 +5,12 @@ use log::trace; // TODO: "Amount remaining" wrapper -pub fn lines(args: CommandArgs) -> Result { +pub fn lines(args: CommandArgs, registry: &CommandRegistry) -> Result { + let args = args.evaluate_once(registry)?; + let span = args.name_span(); let input = args.input; - let span = args.call_info.name_span; + + let input: InputStream = trace_stream!(target: "nu::trace_stream::lines", "input" = input); let stream = input .values diff --git a/src/commands/ls.rs b/src/commands/ls.rs index 72ef4883b9..baba51a31c 100644 --- a/src/commands/ls.rs +++ b/src/commands/ls.rs @@ -4,8 +4,10 @@ use crate::parser::Spanned; use crate::prelude::*; use std::path::{Path, PathBuf}; -pub fn ls(args: CommandArgs) -> Result { - let env = args.env.lock().unwrap(); +pub fn ls(args: CommandArgs, registry: &CommandRegistry) -> Result { + let env = args.env.clone(); + let env = env.lock().unwrap(); + let args = args.evaluate_once(registry)?; let path = env.path.to_path_buf(); let mut full_path = PathBuf::from(path); match &args.nth(0) { @@ -30,7 +32,7 @@ pub fn ls(args: CommandArgs) -> Result { return Err(ShellError::maybe_labeled_error( e.to_string(), e.to_string(), - args.call_info.name_span, + args.name_span(), )); } } @@ -40,7 +42,7 @@ pub fn ls(args: CommandArgs) -> Result { let mut shell_entries = VecDeque::new(); for entry in entries { - let value = dir_entry_dict(&entry?, args.call_info.name_span)?; + let value = dir_entry_dict(&entry?, args.name_span())?; shell_entries.push_back(ReturnSuccess::value(value)) } Ok(shell_entries.to_output_stream()) diff --git a/src/commands/macros.rs b/src/commands/macros.rs index 44a88c189f..0fe491e32d 100644 --- a/src/commands/macros.rs +++ b/src/commands/macros.rs @@ -36,13 +36,14 @@ macro_rules! command { pub struct $export; impl Command for $export { - fn run(&self, $args: CommandArgs) -> Result { - fn command($args: CommandArgs, ( $($param_name),*, ): ( $($param_type),*, )) -> Result { + fn run(&self, $args: CommandArgs, registry: &CommandRegistry) -> Result { + fn command($args: EvaluatedCommandArgs, ( $($param_name),*, ): ( $($param_type),*, )) -> Result { let output = $body; Ok(output.boxed().to_output_stream()) } + let $args = $args.evaluate_once(registry)?; let tuple = ( $($extract ,)* ); command( $args, tuple ) } diff --git a/src/commands/open.rs b/src/commands/open.rs index 5a4fa1474d..d8d8a4a67a 100644 --- a/src/commands/open.rs +++ b/src/commands/open.rs @@ -1,69 +1,152 @@ use crate::context::SpanSource; use crate::errors::ShellError; use crate::object::{Primitive, Switch, Value}; +use crate::parser::hir::SyntaxType; use crate::parser::parse::span::Span; +use crate::parser::registry::{self, CommandConfig, NamedType, PositionalType}; use crate::prelude::*; +use indexmap::IndexMap; use mime::Mime; use std::path::{Path, PathBuf}; use std::str::FromStr; use uuid::Uuid; -command! { - Open as open(args, path: Spanned, --raw: Switch,) { - let span = args.call_info.name_span; +pub struct Open; - let cwd = args - .env - .lock() - .unwrap() - .path() - .to_path_buf(); +impl Command for Open { + fn run( + &self, + args: CommandArgs, + registry: ®istry::CommandRegistry, + ) -> Result { + let env = args.env.clone(); + let args = args.evaluate_once(registry)?; + let path = >::extract(args.expect_nth(0)?)?; + let raw = args.has("raw"); + let span = args.name_span(); + + let cwd = env.lock().unwrap().path().to_path_buf(); let full_path = PathBuf::from(cwd); - let path_str = path.to_str().ok_or(ShellError::type_error("Path", "invalid path".spanned(path.span)))?; + let path_str = path.to_str().ok_or(ShellError::type_error( + "Path", + "invalid path".spanned(path.span), + ))?; - let (file_extension, contents, contents_span, span_source) = fetch(&full_path, path_str, path.span)?; + let (file_extension, contents, contents_span, span_source) = + fetch(&full_path, path_str, path.span)?; - let file_extension = if raw.is_present() { - None - } else { - file_extension - }; + let file_extension = if raw { None } else { file_extension }; let mut stream = VecDeque::new(); if let Some(uuid) = contents_span.source { // If we have loaded something, track its source - stream.push_back(ReturnSuccess::action(CommandAction::AddSpanSource(uuid, span_source))) + stream.push_back(ReturnSuccess::action(CommandAction::AddSpanSource( + uuid, + span_source, + ))) } match contents { Value::Primitive(Primitive::String(string)) => { - let value = parse_as_value( - file_extension, - string, - contents_span, - span, - )?; + let value = parse_as_value(file_extension, string, contents_span, span)?; match value { - Spanned { item: Value::List(list), .. } => { + Spanned { + item: Value::List(list), + .. + } => { for elem in list { stream.push_back(ReturnSuccess::value(elem)); } } - x => stream.push_back(ReturnSuccess::value(x)) + x => stream.push_back(ReturnSuccess::value(x)), } - }, + } other => stream.push_back(ReturnSuccess::value(other.spanned(contents_span))), }; - stream + Ok(stream.boxed().to_output_stream()) + } + + fn name(&self) -> &str { + "open" + } + + fn config(&self) -> CommandConfig { + let mut named = IndexMap::default(); + named.insert("raw".to_string(), NamedType::Switch); + + CommandConfig { + name: self.name().to_string(), + positional: vec![PositionalType::mandatory("path", SyntaxType::Block)], + rest_positional: false, + named, + is_sink: true, + is_filter: false, + } } } +// command! { +// Open as open(args, path: Spanned, --raw: Switch,) { +// let span = args.name_span(); +// let env = args.env.clone(); + +// let path = env +// .lock() +// .unwrap() +// .path() +// .to_path_buf(); + +// let full_path = PathBuf::from(cwd); + +// let path_str = path.to_str().ok_or(ShellError::type_error("Path", "invalid path".spanned(path.span)))?; + +// let (file_extension, contents, contents_span, span_source) = fetch(&full_path, path_str, path.span)?; + +// let file_extension = if raw.is_present() { +// None +// } else { +// file_extension +// }; + +// let mut stream = VecDeque::new(); + +// if let Some(uuid) = contents_span.source { +// // If we have loaded something, track its source +// stream.push_back(ReturnSuccess::action(CommandAction::AddSpanSource(uuid, span_source))) +// } + +// match contents { +// Value::Primitive(Primitive::String(string)) => { +// let value = parse_as_value( +// file_extension, +// string, +// contents_span, +// span, +// )?; + +// match value { +// Spanned { item: Value::List(list), .. } => { +// for elem in list { +// stream.push_back(ReturnSuccess::value(elem)); +// } +// } +// x => stream.push_back(ReturnSuccess::value(x)) +// } +// }, + +// other => stream.push_back(ReturnSuccess::value(other.spanned(contents_span))), +// }; + +// stream +// } +// } + pub fn fetch( cwd: &PathBuf, location: &str, diff --git a/src/commands/pick.rs b/src/commands/pick.rs index b28660c2a1..c122ceec8b 100644 --- a/src/commands/pick.rs +++ b/src/commands/pick.rs @@ -1,19 +1,30 @@ +use crate::context::CommandRegistry; use crate::errors::ShellError; use crate::object::base::select_fields; use crate::prelude::*; -pub fn pick(args: CommandArgs) -> Result { - if args.len() == 0 { +pub fn pick(args: CommandArgs, registry: &CommandRegistry) -> Result { + let args = args.evaluate_once(registry)?; + let len = args.len(); + let span = args.name_span(); + let (input, args) = args.parts(); + + if len == 0 { return Err(ShellError::maybe_labeled_error( "Pick requires fields", "needs parameter", - args.call_info.name_span, + span, )); } - let fields: Result, _> = args.positional_iter().map(|a| a.as_string()).collect(); + let fields: Result, _> = args + .positional + .iter() + .flatten() + .map(|a| a.as_string()) + .collect(); + let fields = fields?; - let input = args.input; let objects = input .values diff --git a/src/commands/plugin.rs b/src/commands/plugin.rs index 62507ed31f..0b9b060f77 100644 --- a/src/commands/plugin.rs +++ b/src/commands/plugin.rs @@ -41,8 +41,12 @@ pub struct PluginCommand { } impl Command for PluginCommand { - fn run(&self, args: CommandArgs) -> Result { - filter_plugin(self.path.clone(), args) + fn run( + &self, + args: CommandArgs, + registry: &CommandRegistry, + ) -> Result { + filter_plugin(self.path.clone(), args, registry) } fn name(&self) -> &str { &self.name @@ -71,7 +75,13 @@ impl Sink for PluginSink { } } -pub fn filter_plugin(path: String, args: CommandArgs) -> Result { +pub fn filter_plugin( + path: String, + args: CommandArgs, + registry: &CommandRegistry, +) -> Result { + let args = args.evaluate_once(registry)?; + let mut child = std::process::Command::new(path) .stdin(std::process::Stdio::piped()) .stdout(std::process::Stdio::piped()) @@ -84,7 +94,7 @@ pub fn filter_plugin(path: String, args: CommandArgs) -> Result Result { +pub fn ps(args: CommandArgs, registry: &CommandRegistry) -> Result { let mut system = sysinfo::System::new_with_specifics(RefreshKind::new().with_processes()); system.refresh_processes(); let list = system.get_process_list(); let list = list .into_iter() - .map(|(_, process)| process_dict(process, args.call_info.name_span)) + .map(|(_, process)| process_dict(process, args.name_span())) .collect::>(); Ok(list.from_input_stream()) diff --git a/src/commands/reject.rs b/src/commands/reject.rs index 7752076295..8801efc6b7 100644 --- a/src/commands/reject.rs +++ b/src/commands/reject.rs @@ -2,21 +2,31 @@ use crate::errors::ShellError; use crate::object::base::reject_fields; use crate::prelude::*; -pub fn reject(args: CommandArgs) -> Result { - let name_span = args.call_info.name_span; +pub fn reject(args: CommandArgs, registry: &CommandRegistry) -> Result { + let name_span = args.name_span(); + let args = args.evaluate_once(registry)?; + let len = args.len(); + let span = args.name_span(); + let (input, args) = args.parts(); - if args.len() == 0 { + if len == 0 { return Err(ShellError::maybe_labeled_error( "Reject requires fields", "needs parameter", - args.call_info.name_span, + span, )); } - let fields: Result, _> = args.positional_iter().map(|a| a.as_string()).collect(); + let fields: Result, _> = args + .positional + .iter() + .flatten() + .map(|a| a.as_string()) + .collect(); + let fields = fields?; - let stream = args.input.values.map(move |item| { + let stream = input.values.map(move |item| { reject_fields(&item, &fields, item.span) .into_spanned_value() .spanned(name_span) diff --git a/src/commands/rm.rs b/src/commands/rm.rs index 5760a34440..5a4ca7cacd 100644 --- a/src/commands/rm.rs +++ b/src/commands/rm.rs @@ -1,3 +1,4 @@ +use crate::commands::EvaluatedStaticCommandArgs; use crate::errors::ShellError; use crate::parser::hir::SyntaxType; use crate::parser::registry::{CommandConfig, NamedType, PositionalType}; @@ -7,8 +8,13 @@ use indexmap::IndexMap; pub struct Remove; impl Command for Remove { - fn run(&self, args: CommandArgs) -> Result { - rm(args) + fn run( + &self, + args: CommandArgs, + registry: &CommandRegistry, + ) -> Result { + let env = args.env.clone(); + rm(args.evaluate_once(registry)?, env) } fn name(&self) -> &str { @@ -30,8 +36,11 @@ impl Command for Remove { } } -pub fn rm(args: CommandArgs) -> Result { - let mut full_path = args.env.lock().unwrap().path().to_path_buf(); +pub fn rm( + args: EvaluatedStaticCommandArgs, + env: Arc>, +) -> Result { + let mut full_path = env.lock().unwrap().path().to_path_buf(); match args .nth(0) @@ -48,7 +57,7 @@ pub fn rm(args: CommandArgs) -> Result { return Err(ShellError::labeled_error( "is a directory", "", - args.call_info.name_span.unwrap(), + args.name_span().unwrap(), )); } std::fs::remove_dir_all(&full_path).expect("can not remove directory"); diff --git a/src/commands/save.rs b/src/commands/save.rs index 866f539f08..4b3152987b 100644 --- a/src/commands/save.rs +++ b/src/commands/save.rs @@ -13,7 +13,7 @@ pub fn save(args: SinkCommandArgs) -> Result<(), ShellError> { return Err(ShellError::maybe_labeled_error( "Save requires a filepath", "needs path", - args.call_info.name_span, + args.name_span(), )); } diff --git a/src/commands/size.rs b/src/commands/size.rs index b255ce4804..1ed87effc0 100644 --- a/src/commands/size.rs +++ b/src/commands/size.rs @@ -2,7 +2,7 @@ use crate::errors::ShellError; use crate::object::{SpannedDictBuilder, Value}; use crate::prelude::*; -pub fn size(args: CommandArgs) -> Result { +pub fn size(args: CommandArgs, registry: &CommandRegistry) -> Result { let input = args.input; Ok(input .values diff --git a/src/commands/skip_while.rs b/src/commands/skip_while.rs index c0aa0f3b07..47df16a480 100644 --- a/src/commands/skip_while.rs +++ b/src/commands/skip_while.rs @@ -6,8 +6,12 @@ use crate::prelude::*; pub struct SkipWhile; impl Command for SkipWhile { - fn run(&self, args: CommandArgs) -> Result { - skip_while(args) + fn run( + &self, + args: CommandArgs, + registry: &CommandRegistry, + ) -> Result { + skip_while(args, registry) } fn name(&self) -> &str { "skip-while" @@ -25,18 +29,24 @@ impl Command for SkipWhile { } } -pub fn skip_while(args: CommandArgs) -> Result { - if args.len() == 0 { +pub fn skip_while( + args: CommandArgs, + registry: &CommandRegistry, +) -> Result { + let args = args.evaluate_once(registry)?; + let block = args.expect_nth(0)?.as_block()?; + let span = args.name_span(); + let len = args.len(); + let input = args.input; + + if len == 0 { return Err(ShellError::maybe_labeled_error( "Where requires a condition", "needs condition", - args.call_info.name_span, + span, )); } - let block = args.nth(0).unwrap().as_block()?; - let input = args.input; - let objects = input.values.skip_while(move |item| { let result = block.invoke(&item); diff --git a/src/commands/sort_by.rs b/src/commands/sort_by.rs index e7e69d6da9..2a8bd07893 100644 --- a/src/commands/sort_by.rs +++ b/src/commands/sort_by.rs @@ -1,11 +1,20 @@ use crate::errors::ShellError; use crate::prelude::*; -pub fn sort_by(args: CommandArgs) -> Result { - let fields: Result, _> = args.positional_iter().map(|a| a.as_string()).collect(); +pub fn sort_by(args: CommandArgs, registry: &CommandRegistry) -> Result { + let args = args.evaluate_once(registry)?; + let (input, args) = args.parts(); + + let fields: Result, _> = args + .positional + .iter() + .flatten() + .map(|a| a.as_string()) + .collect(); + let fields = fields?; - let output = args.input.values.collect::>(); + let output = input.values.collect::>(); let output = output.map(move |mut vec| { vec.sort_by_key(|item| { diff --git a/src/commands/split_column.rs b/src/commands/split_column.rs index 90c12b8f88..f37f9afcb5 100644 --- a/src/commands/split_column.rs +++ b/src/commands/split_column.rs @@ -3,20 +3,24 @@ use crate::object::{Primitive, SpannedDictBuilder, Value}; use crate::prelude::*; use log::trace; -pub fn split_column(args: CommandArgs) -> Result { - let positional: Vec<_> = args.positional_iter().cloned().collect(); - let span = args.call_info.name_span; +pub fn split_column( + args: CommandArgs, + registry: &CommandRegistry, +) -> Result { + let args = args.evaluate_once(registry)?; + let span = args.name_span(); + let (input, args) = args.parts(); + + let positional: Vec<_> = args.positional.iter().flatten().cloned().collect(); if positional.len() == 0 { return Err(ShellError::maybe_labeled_error( "Split-column needs more information", "needs parameter (eg split-column \",\")", - args.call_info.name_span, + span, )); } - let input = args.input; - Ok(input .values .map(move |v| match v.item { diff --git a/src/commands/split_row.rs b/src/commands/split_row.rs index fb530ac8a4..22eedfc6c5 100644 --- a/src/commands/split_row.rs +++ b/src/commands/split_row.rs @@ -4,20 +4,25 @@ use crate::parser::Spanned; use crate::prelude::*; use log::trace; -pub fn split_row(args: CommandArgs) -> Result { - let positional: Vec> = args.positional_iter().cloned().collect(); - let span = args.call_info.name_span; +pub fn split_row( + args: CommandArgs, + registry: &CommandRegistry, +) -> Result { + let args = args.evaluate_once(registry)?; + let span = args.name_span(); + let len = args.len(); + let (input, args) = args.parts(); - if positional.len() == 0 { + let positional: Vec> = args.positional.iter().flatten().cloned().collect(); + + if len == 0 { return Err(ShellError::maybe_labeled_error( "Split-row needs more information", "needs parameter (eg split-row \"\\n\")", - args.call_info.name_span, + span, )); } - let input = args.input; - let stream = input .values .map(move |v| match v.item { diff --git a/src/commands/sysinfo.rs b/src/commands/sysinfo.rs index 425505936f..d0eb2f69c9 100644 --- a/src/commands/sysinfo.rs +++ b/src/commands/sysinfo.rs @@ -6,8 +6,8 @@ use crate::prelude::*; use sys_info::*; use sysinfo::{ComponentExt, DiskExt, NetworkExt, RefreshKind, SystemExt}; -pub fn sysinfo(args: CommandArgs) -> Result { - let name_span = args.call_info.name_span; +pub fn sysinfo(args: CommandArgs, registry: &CommandRegistry) -> Result { + let name_span = args.name_span(); let mut idx = SpannedDictBuilder::new(name_span); if let (Ok(name), Ok(version)) = (os_type(), os_release()) { diff --git a/src/commands/to_array.rs b/src/commands/to_array.rs index 7cea62dbe8..d5fcfa3153 100644 --- a/src/commands/to_array.rs +++ b/src/commands/to_array.rs @@ -1,7 +1,7 @@ use crate::object::Value; use crate::prelude::*; -pub fn to_array(args: CommandArgs) -> Result { +pub fn to_array(args: CommandArgs, registry: &CommandRegistry) -> Result { let out = args.input.values.collect(); Ok(out diff --git a/src/commands/to_csv.rs b/src/commands/to_csv.rs index 04a60590d5..3fdbc49c68 100644 --- a/src/commands/to_csv.rs +++ b/src/commands/to_csv.rs @@ -1,10 +1,9 @@ use crate::object::{Primitive, Value}; use crate::prelude::*; -use log::debug; use csv::WriterBuilder; +use log::debug; pub fn value_to_csv_value(v: &Value) -> Value { - debug!("value_to_csv_value(Value::Object(v)) where v = {:?}", v); match v { @@ -13,7 +12,7 @@ pub fn value_to_csv_value(v: &Value) -> Value { Value::Object(o) => Value::Object(o.clone()), Value::List(l) => Value::List(l.clone()), Value::Block(_) => Value::Primitive(Primitive::Nothing), - _ => Value::Primitive(Primitive::Nothing) + _ => Value::Primitive(Primitive::Nothing), } } @@ -21,7 +20,6 @@ pub fn to_string(v: &Value) -> Result> { match v { Value::List(_l) => return Ok(String::from("[list list]")), Value::Object(o) => { - debug!("to_csv:to_string(Value::Object(v)) where v = {:?}", v); let mut wtr = WriterBuilder::new().from_writer(vec![]); @@ -32,34 +30,33 @@ pub fn to_string(v: &Value) -> Result> { fields.push_back(k.clone()); values.push_back(to_string(&v)?); } - + wtr.write_record(fields).expect("can not write."); wtr.write_record(values).expect("can not write."); - return Ok(String::from_utf8(wtr.into_inner()?)?) - }, + return Ok(String::from_utf8(wtr.into_inner()?)?); + } Value::Primitive(Primitive::String(s)) => return Ok(s.to_string()), - _ => return Err("Bad input".into()) + _ => return Err("Bad input".into()), } } -pub fn to_csv(args: CommandArgs) -> Result { +pub fn to_csv(args: CommandArgs, registry: &CommandRegistry) -> Result { + let args = args.evaluate_once(registry)?; + let name_span = args.name_span(); let out = args.input; - let name_span = args.call_info.name_span; + Ok(out .values - .map( - move |a| match to_string(&value_to_csv_value(&a.item)) { - - Ok(x) => { - ReturnSuccess::value(Value::Primitive(Primitive::String(x)).spanned(name_span)) - } - Err(_) => Err(ShellError::maybe_labeled_error( - "Can not convert to CSV string", - "can not convert piped data to CSV string", - name_span, - )), - }, - ) + .map(move |a| match to_string(&value_to_csv_value(&a.item)) { + Ok(x) => { + ReturnSuccess::value(Value::Primitive(Primitive::String(x)).spanned(name_span)) + } + Err(_) => Err(ShellError::maybe_labeled_error( + "Can not convert to CSV string", + "can not convert piped data to CSV string", + name_span, + )), + }) .to_output_stream()) } diff --git a/src/commands/to_json.rs b/src/commands/to_json.rs index 5cd4c913ce..58e204602b 100644 --- a/src/commands/to_json.rs +++ b/src/commands/to_json.rs @@ -40,9 +40,11 @@ pub fn value_to_json_value(v: &Value) -> serde_json::Value { } } -pub fn to_json(args: CommandArgs) -> Result { +pub fn to_json(args: CommandArgs, registry: &CommandRegistry) -> Result { + let args = args.evaluate_once(registry)?; + let name_span = args.name_span(); let out = args.input; - let name_span = args.call_info.name_span; + Ok(out .values .map( diff --git a/src/commands/to_toml.rs b/src/commands/to_toml.rs index 1551ba78d5..63071590de 100644 --- a/src/commands/to_toml.rs +++ b/src/commands/to_toml.rs @@ -30,9 +30,10 @@ pub fn value_to_toml_value(v: &Value) -> toml::Value { } } -pub fn to_toml(args: CommandArgs) -> Result { +pub fn to_toml(args: CommandArgs, registry: &CommandRegistry) -> Result { + let args = args.evaluate_once(registry)?; + let name_span = args.name_span(); let out = args.input; - let name_span = args.call_info.name_span; Ok(out .values diff --git a/src/commands/to_yaml.rs b/src/commands/to_yaml.rs index 66db5afa40..833791da55 100644 --- a/src/commands/to_yaml.rs +++ b/src/commands/to_yaml.rs @@ -38,9 +38,10 @@ pub fn value_to_yaml_value(v: &Value) -> serde_yaml::Value { } } -pub fn to_yaml(args: CommandArgs) -> Result { +pub fn to_yaml(args: CommandArgs, registry: &CommandRegistry) -> Result { + let args = args.evaluate_once(registry)?; + let name_span = args.name_span(); let out = args.input; - let name_span = args.call_info.name_span; Ok(out .values .map( diff --git a/src/commands/trim.rs b/src/commands/trim.rs index 838875faa0..5224a23923 100644 --- a/src/commands/trim.rs +++ b/src/commands/trim.rs @@ -2,7 +2,7 @@ use crate::errors::ShellError; use crate::object::Value; use crate::prelude::*; -pub fn trim(args: CommandArgs) -> Result { +pub fn trim(args: CommandArgs, registry: &CommandRegistry) -> Result { let input = args.input; Ok(input diff --git a/src/commands/where_.rs b/src/commands/where_.rs index 0b7b818c8c..f852b46a54 100644 --- a/src/commands/where_.rs +++ b/src/commands/where_.rs @@ -1,23 +1,76 @@ use crate::errors::ShellError; -use crate::object::Block; +use crate::object::base as value; +use crate::object::{types, Value}; +use crate::parser::hir::SyntaxType; +use crate::parser::registry::{self, CommandConfig, PositionalType}; use crate::prelude::*; + use futures::future::ready; +use indexmap::IndexMap; use log::trace; -command! { - Where as where(args, condition: Block,) { - let input: InputStream = trace_stream!(target: "nu::trace_stream::where", "where input" = args.input); +pub struct Where; - input.values.filter_map(move |item| { - let result = condition.invoke(&item); +impl Command for Where { + fn run( + &self, + args: CommandArgs, + registry: ®istry::CommandRegistry, + ) -> Result { + let args = args.evaluate_once(registry)?; + let condition = value::Block::extract(args.expect_nth(0)?)?; + let input = args.input; + let input: InputStream = + trace_stream!(target: "nu::trace_stream::where", "where input" = input); - let return_value = match result { - Err(err) => Some(Err(err)), - Ok(v) if v.is_true() => Some(Ok(ReturnSuccess::Value(item.clone()))), - _ => None, - }; + Ok(input + .values + .filter_map(move |item| { + let result = condition.invoke(&item); - ready(return_value) - }) + let return_value = match result { + Err(err) => Some(Err(err)), + Ok(v) if v.is_true() => Some(Ok(ReturnSuccess::Value(item.clone()))), + _ => None, + }; + + ready(return_value) + }) + .boxed() + .to_output_stream()) + } + + fn name(&self) -> &str { + "where" + } + + fn config(&self) -> CommandConfig { + CommandConfig { + name: self.name().to_string(), + positional: vec![PositionalType::mandatory("condition", SyntaxType::Block)], + rest_positional: false, + named: IndexMap::default(), + is_sink: true, + is_filter: false, + } } } + +// command! { +// Where as where(args, condition: Block,) { +// let input = args.input; +// let input: InputStream = trace_stream!(target: "nu::trace_stream::where", "where input" = input); + +// input.values.filter_map(move |item| { +// let result = condition.invoke(&item); + +// let return_value = match result { +// Err(err) => Some(Err(err)), +// Ok(v) if v.is_true() => Some(Ok(ReturnSuccess::Value(item.clone()))), +// _ => None, +// }; + +// ready(return_value) +// }) +// } +// } diff --git a/src/context.rs b/src/context.rs index 9be7463a0f..2f9d00c98f 100644 --- a/src/context.rs +++ b/src/context.rs @@ -1,21 +1,24 @@ -use crate::commands::command::{CallInfo, Sink, SinkCommandArgs}; +use crate::commands::command::{CallInfo, Sink, SinkCommandArgs, UnevaluatedCallInfo}; use crate::parser::{ - registry::{Args, CommandConfig, CommandRegistry}, + hir, + registry::{self, CommandConfig}, Span, }; use crate::prelude::*; -use serde::{Deserialize, Serialize}; -use uuid::Uuid; +use derive_new::new; use indexmap::IndexMap; +use serde::{Deserialize, Serialize}; use std::collections::HashMap; use std::error::Error; use std::sync::Arc; +use uuid::Uuid; #[derive(Clone, Debug, Serialize, Deserialize)] pub enum SpanSource { Url(String), File(String), + Source(Text), } #[derive(Clone, Debug, Serialize, Deserialize)] @@ -35,9 +38,51 @@ impl SourceMap { } } +#[derive(Clone, new)] +pub struct CommandRegistry { + #[new(value = "Arc::new(Mutex::new(IndexMap::default()))")] + registry: Arc>>>, +} + +impl CommandRegistry { + crate fn empty() -> CommandRegistry { + CommandRegistry { + registry: Arc::new(Mutex::new(IndexMap::default())), + } + } + + fn get_config(&self, name: &str) -> Option { + let registry = self.registry.lock().unwrap(); + + registry.get(name).map(|c| c.config()) + } + + fn get_command(&self, name: &str) -> Option> { + let registry = self.registry.lock().unwrap(); + + registry.get(name).map(|c| c.clone()) + } + + fn has(&self, name: &str) -> bool { + let registry = self.registry.lock().unwrap(); + + registry.contains_key(name) + } + + fn insert(&mut self, name: impl Into, command: Arc) { + let mut registry = self.registry.lock().unwrap(); + registry.insert(name.into(), command); + } + + crate fn names(&self) -> Vec { + let mut registry = self.registry.lock().unwrap(); + registry.keys().cloned().collect() + } +} + #[derive(Clone)] pub struct Context { - commands: IndexMap>, + registry: CommandRegistry, sinks: IndexMap>, crate source_map: SourceMap, crate host: Arc>, @@ -45,9 +90,13 @@ pub struct Context { } impl Context { + crate fn registry(&self) -> &CommandRegistry { + &self.registry + } + crate fn basic() -> Result> { Ok(Context { - commands: indexmap::IndexMap::new(), + registry: CommandRegistry::new(), sinks: indexmap::IndexMap::new(), source_map: SourceMap::new(), host: Arc::new(Mutex::new(crate::env::host::BasicHost)), @@ -57,7 +106,7 @@ impl Context { pub fn add_commands(&mut self, commands: Vec>) { for command in commands { - self.commands.insert(command.name().to_string(), command); + self.registry.insert(command.name().to_string(), command); } } @@ -83,7 +132,7 @@ impl Context { &mut self, command: Arc, name_span: Option, - args: Args, + args: registry::EvaluatedArgs, input: Vec>, ) -> Result<(), ShellError> { let command_args = SinkCommandArgs { @@ -99,16 +148,16 @@ impl Context { command.run(command_args) } - pub fn clone_commands(&self) -> indexmap::IndexMap> { - self.commands.clone() + pub fn clone_commands(&self) -> CommandRegistry { + self.registry.clone() } crate fn has_command(&self, name: &str) -> bool { - self.commands.contains_key(name) + self.registry.has(name) } crate fn get_command(&self, name: &str) -> Arc { - self.commands.get(name).unwrap().clone() + self.registry.get_command(name).unwrap() } crate fn run_command( @@ -116,26 +165,43 @@ impl Context { command: Arc, name_span: Option, source_map: SourceMap, - args: Args, + args: hir::Call, + source: Text, input: InputStream, ) -> Result { - let command_args = CommandArgs { + let command_args = self.command_args(args, input, source, source_map, name_span); + + command.run(command_args, self.registry()) + } + + fn call_info( + &self, + args: hir::Call, + source: Text, + source_map: SourceMap, + name_span: Option, + ) -> UnevaluatedCallInfo { + UnevaluatedCallInfo { + args, + source, + source_map, + name_span, + } + } + + fn command_args( + &self, + args: hir::Call, + input: InputStream, + source: Text, + source_map: SourceMap, + name_span: Option, + ) -> CommandArgs { + CommandArgs { host: self.host.clone(), env: self.env.clone(), - call_info: CallInfo { - name_span, - source_map, - args, - }, + call_info: self.call_info(args, source, source_map, name_span), input, - }; - - command.run(command_args) - } -} - -impl CommandRegistry for Context { - fn get(&self, name: &str) -> Option { - self.commands.get(name).map(|c| c.config()) + } } } diff --git a/src/env/host.rs b/src/env/host.rs index 8033f86dd2..3ae79849ca 100644 --- a/src/env/host.rs +++ b/src/env/host.rs @@ -2,7 +2,7 @@ use crate::prelude::*; use language_reporting::termcolor; use std::fmt::Debug; -pub trait Host: Debug { +pub trait Host: Debug + Send { fn out_terminal(&self) -> Box; fn err_terminal(&self) -> Box; diff --git a/src/evaluate/evaluator.rs b/src/evaluate/evaluator.rs index a8a3915e97..563ccb1868 100644 --- a/src/evaluate/evaluator.rs +++ b/src/evaluate/evaluator.rs @@ -9,7 +9,7 @@ use derive_new::new; use indexmap::IndexMap; #[derive(new)] -crate struct Scope { +pub struct Scope { it: Spanned, #[new(default)] vars: IndexMap>, @@ -22,16 +22,26 @@ impl Scope { vars: IndexMap::new(), } } + + crate fn it_value(value: Spanned) -> Scope { + Scope { + it: value, + vars: IndexMap::new(), + } + } } crate fn evaluate_baseline_expr( expr: &Expression, - registry: &dyn CommandRegistry, + registry: &CommandRegistry, scope: &Scope, source: &Text, ) -> Result, ShellError> { match &expr.item { RawExpression::Literal(literal) => Ok(evaluate_literal(expr.copy_span(*literal), source)), + RawExpression::Synthetic(hir::Synthetic::String(s)) => { + Ok(Value::string(s).spanned_unknown()) + } RawExpression::Variable(var) => evaluate_reference(var, scope, source), RawExpression::Binary(binary) => { let left = evaluate_baseline_expr(binary.left(), registry, scope, source)?; diff --git a/src/lib.rs b/src/lib.rs index f5f7f90f79..15e3bf3ef1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -23,6 +23,7 @@ mod parser; mod plugin; mod shell; mod stream; +mod traits; pub use crate::commands::command::{CallInfo, ReturnSuccess, ReturnValue}; pub use crate::context::SpanSource; @@ -34,4 +35,4 @@ pub use cli::cli; pub use errors::ShellError; pub use object::base::{Primitive, Value}; pub use parser::parse::text::Text; -pub use parser::registry::{Args, CommandConfig, NamedType, PositionalType}; +pub use parser::registry::{CommandConfig, EvaluatedArgs, NamedType, PositionalType}; diff --git a/src/object/base.rs b/src/object/base.rs index 168bbdd8ea..6542ecaac2 100644 --- a/src/object/base.rs +++ b/src/object/base.rs @@ -1,3 +1,4 @@ +use crate::context::CommandRegistry; use crate::errors::ShellError; use crate::evaluate::{evaluate_baseline_expr, Scope}; use crate::object::SpannedDictBuilder; @@ -169,7 +170,12 @@ impl Block { let mut last = None; for expr in self.expressions.iter() { - last = Some(evaluate_baseline_expr(&expr, &(), &scope, &self.source)?) + last = Some(evaluate_baseline_expr( + &expr, + &CommandRegistry::empty(), + &scope, + &self.source, + )?) } Ok(last.unwrap()) diff --git a/src/parser/hir.rs b/src/parser/hir.rs index 7b0db75084..7dd6ac2034 100644 --- a/src/parser/hir.rs +++ b/src/parser/hir.rs @@ -4,9 +4,13 @@ crate mod binary; crate mod named; crate mod path; -use crate::parser::{Span, Spanned, Unit}; +use crate::evaluate::Scope; +use crate::parser::{registry, Span, Spanned, Unit}; +use crate::prelude::*; use derive_new::new; use getset::Getters; +use serde_derive::{Deserialize, Serialize}; +use std::fmt; crate use baseline_parse::{baseline_parse_single_token, baseline_parse_token_as_string}; crate use baseline_parse_tokens::{baseline_parse_next_expr, SyntaxType, TokensIterator}; @@ -23,7 +27,7 @@ pub fn path(head: impl Into, tail: Vec>>) ) } -#[derive(Debug, Clone, Eq, PartialEq, Getters, new)] +#[derive(Debug, Clone, Eq, PartialEq, Getters, Serialize, Deserialize, new)] pub struct Call { #[get = "crate"] head: Box, @@ -33,9 +37,42 @@ pub struct Call { named: Option, } -#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] +impl Call { + pub fn evaluate( + &self, + registry: ®istry::CommandRegistry, + scope: &Scope, + source: &Text, + ) -> Result { + registry::evaluate_args(self, registry, scope, source) + } +} + +impl ToDebug for Call { + fn fmt_debug(&self, f: &mut fmt::Formatter, source: &str) -> fmt::Result { + write!(f, "({}", self.head.debug(source))?; + + if let Some(positional) = &self.positional { + write!(f, " ")?; + write!( + f, + "{}", + &itertools::join(positional.iter().map(|p| p.debug(source)), " ") + )?; + } + + if let Some(named) = &self.named { + write!(f, "{}", named.debug(source))?; + } + + Ok(()) + } +} + +#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize, Deserialize)] pub enum RawExpression { Literal(Literal), + Synthetic(Synthetic), Variable(Variable), Binary(Box), Block(Vec), @@ -45,10 +82,24 @@ pub enum RawExpression { Boolean(bool), } +#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize, Deserialize)] +pub enum Synthetic { + String(String), +} + +impl Synthetic { + pub fn type_name(&self) -> &'static str { + match self { + Synthetic::String(_) => "string", + } + } +} + impl RawExpression { pub fn type_name(&self) -> &'static str { match self { RawExpression::Literal(literal) => literal.type_name(), + RawExpression::Synthetic(synthetic) => synthetic.type_name(), RawExpression::Variable(..) => "variable", RawExpression::Binary(..) => "binary", RawExpression::Block(..) => "block", @@ -61,36 +112,40 @@ impl RawExpression { pub type Expression = Spanned; impl Expression { - fn int(i: impl Into, span: impl Into) -> Expression { + crate fn int(i: impl Into, span: impl Into) -> Expression { Spanned::from_item(RawExpression::Literal(Literal::Integer(i.into())), span) } - fn size(i: impl Into, unit: impl Into, span: impl Into) -> Expression { + crate fn size(i: impl Into, unit: impl Into, span: impl Into) -> Expression { Spanned::from_item( RawExpression::Literal(Literal::Size(i.into(), unit.into())), span, ) } - fn string(inner: impl Into, outer: impl Into) -> Expression { + crate fn synthetic_string(s: impl Into) -> Expression { + RawExpression::Synthetic(Synthetic::String(s.into())).spanned_unknown() + } + + crate fn string(inner: impl Into, outer: impl Into) -> Expression { Spanned::from_item( RawExpression::Literal(Literal::String(inner.into())), outer.into(), ) } - fn bare(span: impl Into) -> Expression { + crate fn bare(span: impl Into) -> Expression { Spanned::from_item(RawExpression::Literal(Literal::Bare), span.into()) } - fn variable(inner: impl Into, outer: impl Into) -> Expression { + crate fn variable(inner: impl Into, outer: impl Into) -> Expression { Spanned::from_item( RawExpression::Variable(Variable::Other(inner.into())), outer.into(), ) } - fn it_variable(inner: impl Into, outer: impl Into) -> Expression { + crate fn it_variable(inner: impl Into, outer: impl Into) -> Expression { Spanned::from_item( RawExpression::Variable(Variable::It(inner.into())), outer.into(), @@ -98,13 +153,37 @@ impl Expression { } } +impl ToDebug for Expression { + fn fmt_debug(&self, f: &mut fmt::Formatter, source: &str) -> fmt::Result { + match self.item() { + RawExpression::Literal(l) => write!(f, "{}", l.spanned(self.span()).debug(source)), + RawExpression::Synthetic(Synthetic::String(s)) => write!(f, "{:?}", s), + RawExpression::Variable(Variable::It(_)) => write!(f, "$it"), + RawExpression::Variable(Variable::Other(s)) => write!(f, "${}", s.slice(source)), + RawExpression::Binary(b) => write!(f, "{}", b.debug(source)), + RawExpression::Block(exprs) => { + write!(f, "{{ ")?; + + for expr in exprs { + write!(f, "{} ", expr.debug(source))?; + } + + write!(f, "}}") + } + RawExpression::Path(p) => write!(f, "{}", p.debug(source)), + RawExpression::Boolean(true) => write!(f, "$yes"), + RawExpression::Boolean(false) => write!(f, "$no"), + } + } +} + impl From> for Expression { fn from(path: Spanned) -> Expression { path.map(|p| RawExpression::Path(Box::new(p))) } } -#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)] +#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize, Deserialize)] pub enum Literal { Integer(i64), Size(i64, Unit), @@ -112,6 +191,17 @@ pub enum Literal { Bare, } +impl ToDebug for Spanned<&Literal> { + fn fmt_debug(&self, f: &mut fmt::Formatter, source: &str) -> fmt::Result { + match self.item() { + Literal::Integer(int) => write!(f, "{}", *int), + Literal::Size(int, unit) => write!(f, "{}{:?}", *int, unit), + Literal::String(span) => write!(f, "{}", span.slice(source)), + Literal::Bare => write!(f, "{}", self.span().slice(source)), + } + } +} + impl Literal { fn type_name(&self) -> &'static str { match self { @@ -123,7 +213,7 @@ impl Literal { } } -#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] +#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize, Deserialize)] pub enum Variable { It(Span), Other(Span), diff --git a/src/parser/hir/baseline_parse_tokens.rs b/src/parser/hir/baseline_parse_tokens.rs index 8b8dd026d0..a743fc0368 100644 --- a/src/parser/hir/baseline_parse_tokens.rs +++ b/src/parser/hir/baseline_parse_tokens.rs @@ -12,7 +12,7 @@ use serde_derive::{Deserialize, Serialize}; pub fn baseline_parse_tokens( token_nodes: &mut TokensIterator<'_>, - registry: &dyn CommandRegistry, + registry: &CommandRegistry, source: &Text, ) -> Result, ShellError> { let mut exprs: Vec = vec![]; @@ -43,7 +43,7 @@ pub enum SyntaxType { pub fn baseline_parse_next_expr( tokens: &mut TokensIterator, - registry: &dyn CommandRegistry, + registry: &CommandRegistry, source: &Text, syntax_type: SyntaxType, ) -> Result { @@ -176,7 +176,7 @@ pub fn baseline_parse_next_expr( pub fn baseline_parse_semantic_token( token: &TokenNode, - registry: &dyn CommandRegistry, + registry: &CommandRegistry, source: &Text, ) -> Result { match token { @@ -197,7 +197,7 @@ pub fn baseline_parse_semantic_token( pub fn baseline_parse_delimited( token: &Spanned, - registry: &dyn CommandRegistry, + registry: &CommandRegistry, source: &Text, ) -> Result { match token.delimiter() { @@ -216,7 +216,7 @@ pub fn baseline_parse_delimited( pub fn baseline_parse_path( token: &Spanned, - registry: &dyn CommandRegistry, + registry: &CommandRegistry, source: &Text, ) -> Result { let head = baseline_parse_semantic_token(token.head(), registry, source)?; diff --git a/src/parser/hir/binary.rs b/src/parser/hir/binary.rs index 315964ed5f..dd0f078716 100644 --- a/src/parser/hir/binary.rs +++ b/src/parser/hir/binary.rs @@ -1,11 +1,26 @@ use crate::parser::{hir::Expression, Operator, Spanned}; +use crate::prelude::*; use derive_new::new; use getset::Getters; +use serde_derive::{Deserialize, Serialize}; +use std::fmt; -#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Getters, new)] +#[derive( + Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Getters, Serialize, Deserialize, new, +)] #[get = "crate"] pub struct Binary { left: Expression, op: Spanned, right: Expression, } + +impl ToDebug for Binary { + fn fmt_debug(&self, f: &mut fmt::Formatter, source: &str) -> fmt::Result { + write!(f, "{}", self.left.debug(source))?; + write!(f, " {} ", self.op.debug(source))?; + write!(f, "{}", self.right.debug(source))?; + + Ok(()) + } +} diff --git a/src/parser/hir/named.rs b/src/parser/hir/named.rs index 07c6db655e..317ecc93cb 100644 --- a/src/parser/hir/named.rs +++ b/src/parser/hir/named.rs @@ -1,10 +1,13 @@ use crate::parser::hir::Expression; use crate::parser::{Flag, Span}; +use crate::prelude::*; use derive_new::new; use indexmap::IndexMap; use log::trace; +use serde_derive::{Deserialize, Serialize}; +use std::fmt; -#[derive(Debug, Clone, Eq, PartialEq)] +#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)] pub enum NamedValue { AbsentSwitch, PresentSwitch(Span), @@ -12,12 +15,27 @@ pub enum NamedValue { Value(Expression), } -#[derive(Debug, Clone, Eq, PartialEq, new)] +#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize, new)] pub struct NamedArguments { #[new(default)] crate named: IndexMap, } +impl ToDebug for NamedArguments { + fn fmt_debug(&self, f: &mut fmt::Formatter, source: &str) -> fmt::Result { + for (name, value) in &self.named { + match value { + NamedValue::AbsentSwitch => continue, + NamedValue::PresentSwitch(span) => write!(f, " {}", span.slice(source))?, + NamedValue::AbsentValue => continue, + NamedValue::Value(expr) => write!(f, " --{} {}", name, expr.debug(source))?, + } + } + + Ok(()) + } +} + impl NamedArguments { pub fn insert_switch(&mut self, name: impl Into, switch: Option) { let name = name.into(); diff --git a/src/parser/hir/path.rs b/src/parser/hir/path.rs index 02bf8e5ce3..18577a4e25 100644 --- a/src/parser/hir/path.rs +++ b/src/parser/hir/path.rs @@ -1,10 +1,27 @@ use crate::parser::{hir::Expression, Spanned}; +use crate::prelude::*; use derive_new::new; use getset::Getters; +use serde_derive::{Deserialize, Serialize}; +use std::fmt; -#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Getters, new)] +#[derive( + Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Getters, Serialize, Deserialize, new, +)] #[get = "crate"] pub struct Path { head: Expression, tail: Vec>, } + +impl ToDebug for Path { + fn fmt_debug(&self, f: &mut fmt::Formatter, source: &str) -> fmt::Result { + write!(f, "{}", self.head.debug(source))?; + + for part in &self.tail { + write!(f, ".{}", part.item())?; + } + + Ok(()) + } +} diff --git a/src/parser/parse/operator.rs b/src/parser/parse/operator.rs index c5ce66bfd8..0933452c7c 100644 --- a/src/parser/parse/operator.rs +++ b/src/parser/parse/operator.rs @@ -1,4 +1,6 @@ +use crate::prelude::*; use serde_derive::{Deserialize, Serialize}; +use std::fmt; use std::str::FromStr; #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Deserialize, Serialize)] @@ -11,6 +13,12 @@ pub enum Operator { GreaterThanOrEqual, } +impl ToDebug for Operator { + fn fmt_debug(&self, f: &mut fmt::Formatter, _source: &str) -> fmt::Result { + write!(f, "{}", self.as_str()) + } +} + impl Operator { #[allow(unused)] pub fn print(&self) -> String { diff --git a/src/parser/parse/span.rs b/src/parser/parse/span.rs index 7d6aa7334d..29b1f1a0ce 100644 --- a/src/parser/parse/span.rs +++ b/src/parser/parse/span.rs @@ -1,3 +1,4 @@ +use crate::prelude::*; use crate::Text; use derive_new::new; use getset::Getters; @@ -14,6 +15,12 @@ pub struct Spanned { pub item: T, } +impl HasSpan for Spanned { + fn span(&self) -> Span { + self.span + } +} + impl Spanned { pub fn spanned(self, span: impl Into) -> Spanned { Spanned::from_item(self.item, span.into()) diff --git a/src/parser/parse/text.rs b/src/parser/parse/text.rs index 60f014d9eb..b17092dc17 100644 --- a/src/parser/parse/text.rs +++ b/src/parser/parse/text.rs @@ -1,3 +1,4 @@ +use serde::{Deserialize, Deserializer, Serialize, Serializer}; use std::cmp::Ordering; use std::hash::Hash; use std::hash::Hasher; @@ -202,3 +203,21 @@ where self.partial_cmp(*other) } } + +impl Serialize for Text { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + self.as_ref().serialize(serializer) + } +} + +impl Deserialize<'de> for Text { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + Ok(Text::from(String::deserialize(deserializer)?)) + } +} diff --git a/src/parser/parse_command.rs b/src/parser/parse_command.rs index 16a9c88169..a5a2b0fa4f 100644 --- a/src/parser/parse_command.rs +++ b/src/parser/parse_command.rs @@ -10,7 +10,7 @@ use log::trace; pub fn parse_command( config: &CommandConfig, - registry: &dyn CommandRegistry, + registry: &CommandRegistry, call: &Spanned, source: &Text, ) -> Result { @@ -63,7 +63,7 @@ fn parse_command_head(head: &TokenNode) -> Result { fn parse_command_tail( config: &CommandConfig, - registry: &dyn CommandRegistry, + registry: &CommandRegistry, tail: Option>, source: &Text, command_span: Span, diff --git a/src/parser/registry.rs b/src/parser/registry.rs index 804dfe48cd..a0d0bf221b 100644 --- a/src/parser/registry.rs +++ b/src/parser/registry.rs @@ -1,3 +1,5 @@ +// TODO: Temporary redirect +crate use crate::context::CommandRegistry; use crate::evaluate::{evaluate_baseline_expr, Scope}; use crate::parser::{hir, hir::SyntaxType, parse_command, CallNode, Spanned}; use crate::prelude::*; @@ -80,17 +82,17 @@ pub struct CommandConfig { } #[derive(Debug, Default, new, Serialize, Deserialize)] -pub struct Args { +pub struct EvaluatedArgs { pub positional: Option>>, pub named: Option>>, } #[derive(new)] -pub struct DebugPositional<'a> { +pub struct DebugEvaluatedPositional<'a> { positional: &'a Option>>, } -impl fmt::Debug for DebugPositional<'a> { +impl fmt::Debug for DebugEvaluatedPositional<'a> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match &self.positional { None => write!(f, "None"), @@ -103,11 +105,11 @@ impl fmt::Debug for DebugPositional<'a> { } #[derive(new)] -pub struct DebugNamed<'a> { +pub struct DebugEvaluatedNamed<'a> { named: &'a Option>>, } -impl fmt::Debug for DebugNamed<'a> { +impl fmt::Debug for DebugEvaluatedNamed<'a> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match &self.named { None => write!(f, "None"), @@ -119,24 +121,27 @@ impl fmt::Debug for DebugNamed<'a> { } } -pub struct DebugArgs<'a> { - args: &'a Args, +pub struct DebugEvaluatedArgs<'a> { + args: &'a EvaluatedArgs, } -impl fmt::Debug for DebugArgs<'a> { +impl fmt::Debug for DebugEvaluatedArgs<'a> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let mut s = f.debug_struct("Args"); - s.field("positional", &DebugPositional::new(&self.args.positional)); - s.field("named", &DebugNamed::new(&self.args.named)); + s.field( + "positional", + &DebugEvaluatedPositional::new(&self.args.positional), + ); + s.field("named", &DebugEvaluatedNamed::new(&self.args.named)); s.finish() } } -impl Args { - pub fn debug(&'a self) -> DebugArgs<'a> { - DebugArgs { args: self } +impl EvaluatedArgs { + pub fn debug(&'a self) -> DebugEvaluatedArgs<'a> { + DebugEvaluatedArgs { args: self } } pub fn nth(&self, pos: usize) -> Option<&Spanned> { @@ -205,92 +210,17 @@ impl Iterator for PositionalIter<'a> { } impl CommandConfig { - crate fn evaluate_args( + crate fn parse_args( &self, call: &Spanned, - registry: &dyn CommandRegistry, - scope: &Scope, + registry: &CommandRegistry, source: &Text, - ) -> Result { + ) -> Result { let args = parse_command(self, registry, call, source)?; trace!("parsed args: {:?}", args); - evaluate_args(args, registry, scope, source) - - // let mut positional: Vec> = vec![]; - // let mut named: IndexMap = IndexMap::default(); - - // let mut args: Vec = args.cloned().collect(); - - // for (key, ty) in self.named.iter() { - // let index = args.iter().position(|a| a.is_flag(&key, source)); - - // match (index, ty) { - // (Some(i), NamedType::Switch) => { - // args.remove(i); - // named.insert(key.clone(), Value::boolean(true)); - // } - - // (None, NamedType::Switch) => {} - - // (Some(i), NamedType::Optional(v)) => { - // args.remove(i); - // named.insert(key.clone(), extract_named(&mut args, i, v)?); - // } - - // (None, NamedType::Optional(_)) => {} - - // (Some(i), NamedType::Mandatory(v)) => { - // args.remove(i); - // named.insert(key.clone(), extract_named(&mut args, i, v)?); - // } - - // (None, NamedType::Mandatory(_)) => { - // return Err(ShellError::string(&format!( - // "Expected mandatory argument {}, but it was missing", - // key - // ))) - // } - // } - // } - - // let mut args = args.into_iter(); - - // for param in &self.mandatory_positional { - // let arg = args.next(); - - // let value = match arg { - // None => { - // return Err(ShellError::string(format!( - // "expected mandatory positional argument {}", - // param.name() - // ))) - // } - - // Some(arg) => param.evaluate(arg.clone(), scope, source)?, - // }; - - // positional.push(value); - // } - - // if self.rest_positional { - // let rest: Result>, _> = args - // .map(|i| evaluate_baseline_expr(&i, &Scope::empty(), source)) - // .collect(); - // positional.extend(rest?); - // } else { - // let rest: Vec = args.collect(); - - // if rest.len() > 0 { - // return Err(ShellError::string(&format!( - // "Too many arguments, extras: {:?}", - // rest - // ))); - // } - // } - - // Ok(Args { positional, named }) + Ok(args) } #[allow(unused)] @@ -299,25 +229,25 @@ impl CommandConfig { } } -fn evaluate_args( - args: hir::Call, - registry: &dyn CommandRegistry, +crate fn evaluate_args( + call: &hir::Call, + registry: &CommandRegistry, scope: &Scope, source: &Text, -) -> Result { - let positional: Result>, _> = args +) -> Result { + let positional: Result>, _> = call .positional() .as_ref() .map(|p| { p.iter() - .map(|e| evaluate_baseline_expr(e, &(), scope, source)) + .map(|e| evaluate_baseline_expr(e, &CommandRegistry::empty(), scope, source)) .collect() }) .transpose(); let positional = positional?; - let named: Result>>, ShellError> = args + let named: Result>>, ShellError> = call .named() .as_ref() .map(|n| { @@ -348,15 +278,5 @@ fn evaluate_args( let named = named?; - Ok(Args::new(positional, named)) -} - -pub trait CommandRegistry { - fn get(&self, name: &str) -> Option; -} - -impl CommandRegistry for () { - fn get(&self, _name: &str) -> Option { - None - } + Ok(EvaluatedArgs::new(positional, named)) } diff --git a/src/prelude.rs b/src/prelude.rs index 509a0866ac..b6f3f5f112 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -34,9 +34,10 @@ macro_rules! trace_stream { crate use crate::cli::MaybeOwned; crate use crate::commands::command::{ - Command, CommandAction, CommandArgs, ReturnSuccess, ReturnValue, Sink, SinkCommandArgs, + Command, CommandAction, CommandArgs, EvaluatedCommandArgs, ReturnSuccess, ReturnValue, Sink, + SinkCommandArgs, }; -crate use crate::context::Context; +crate use crate::context::{CommandRegistry, Context}; crate use crate::env::host::handle_unexpected; crate use crate::env::{Environment, Host}; crate use crate::errors::ShellError; @@ -44,10 +45,10 @@ crate use crate::object::types::ExtractType; crate use crate::object::{Primitive, Value}; crate use crate::parser::{Span, Spanned, SpannedItem}; crate use crate::stream::{InputStream, OutputStream}; +crate use crate::traits::{HasSpan, ToDebug}; crate use crate::Text; crate use futures::stream::BoxStream; -crate use futures::Stream; -crate use futures::{FutureExt, StreamExt}; +crate use futures::{FutureExt, Stream, StreamExt}; crate use std::collections::VecDeque; crate use std::future::Future; crate use std::sync::{Arc, Mutex}; diff --git a/src/shell/completer.rs b/src/shell/completer.rs index bcd8577af8..af1276e835 100644 --- a/src/shell/completer.rs +++ b/src/shell/completer.rs @@ -1,4 +1,6 @@ +use crate::context::CommandRegistry; use crate::prelude::*; + use derive_new::new; use rustyline::completion::Completer; use rustyline::completion::{self, FilenameCompleter}; @@ -7,7 +9,7 @@ use rustyline::line_buffer::LineBuffer; #[derive(new)] crate struct NuCompleter { pub file_completer: FilenameCompleter, - pub commands: indexmap::IndexMap>, + pub commands: CommandRegistry, } impl Completer for NuCompleter { @@ -19,7 +21,7 @@ impl Completer for NuCompleter { pos: usize, context: &rustyline::Context, ) -> rustyline::Result<(usize, Vec)> { - let commands: Vec = self.commands.keys().cloned().collect(); + let commands: Vec = self.commands.names(); let mut completions = self.file_completer.complete(line, pos, context)?.1; diff --git a/src/shell/helper.rs b/src/shell/helper.rs index 809b8c0655..06cc97b380 100644 --- a/src/shell/helper.rs +++ b/src/shell/helper.rs @@ -18,7 +18,7 @@ crate struct Helper { } impl Helper { - crate fn new(commands: indexmap::IndexMap>) -> Helper { + crate fn new(commands: CommandRegistry) -> Helper { Helper { completer: NuCompleter { file_completer: FilenameCompleter::new(), diff --git a/src/traits.rs b/src/traits.rs new file mode 100644 index 0000000000..ba67bddfff --- /dev/null +++ b/src/traits.rs @@ -0,0 +1,28 @@ +use crate::prelude::*; +use std::fmt; + +pub struct Debuggable<'a, T: ToDebug> { + inner: &'a T, + source: &'a str, +} + +impl fmt::Display for Debuggable<'a, T> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + self.inner.fmt_debug(f, self.source) + } +} + +pub trait HasSpan { + fn span(&self) -> Span; +} + +pub trait ToDebug: Sized { + fn debug(&'a self, source: &'a str) -> Debuggable<'a, Self> { + Debuggable { + inner: self, + source, + } + } + + fn fmt_debug(&self, f: &mut fmt::Formatter, source: &str) -> fmt::Result; +} diff --git a/tests/fixtures/nuplayground/.gitignore b/tests/fixtures/nuplayground/.gitignore deleted file mode 100644 index 09fb65d850..0000000000 --- a/tests/fixtures/nuplayground/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -*_test -*.txt diff --git a/tests/helpers/mod.rs b/tests/helpers/mod.rs index ed54a69dd7..bd9a3106e7 100644 --- a/tests/helpers/mod.rs +++ b/tests/helpers/mod.rs @@ -7,9 +7,9 @@ use std::io::Read; #[macro_export] macro_rules! nu { ($out:ident, $cwd:expr, $commands:expr) => { + pub use std::error::Error; pub use std::io::prelude::*; pub use std::process::{Command, Stdio}; - pub use std::error::Error; let commands = &*format!( " @@ -93,10 +93,11 @@ pub fn setup_playground_for(topic: &str) -> (String, String) { } pub fn file_contents(full_path: &str) -> String { - let mut file = std::fs::File::open(full_path).expect("can not open file"); - let mut contents = String::new(); - file.read_to_string(&mut contents).expect("can not read file"); - contents + let mut file = std::fs::File::open(full_path).expect("can not open file"); + let mut contents = String::new(); + file.read_to_string(&mut contents) + .expect("can not read file"); + contents } pub fn create_file_at(full_path: &str) { @@ -112,7 +113,14 @@ pub fn delete_directory_at(full_path: &str) { } pub fn create_directory_at(full_path: &str) { - std::fs::create_dir(PathBuf::from(full_path)).expect("can not create directory"); + let path = PathBuf::from(full_path); + + println!("{:?} - is_dir: {:?}", path, path.is_dir()); + + if !path.is_dir() { + std::fs::create_dir_all(PathBuf::from(full_path)) + .expect(&format!("can not create directory {:?}", full_path)); + } } pub fn executable_path() -> PathBuf { From 73deeb69dbd77b794c0f36a75b7fa15e84b2ccff Mon Sep 17 00:00:00 2001 From: Yehuda Katz Date: Tue, 23 Jul 2019 21:10:48 -0700 Subject: [PATCH 02/12] Clean up lint errors --- Cargo.lock | 24 ++++++++++ Cargo.toml | 1 + src/commands.rs | 2 +- src/commands/command.rs | 35 ++------------ src/commands/exit.rs | 2 +- src/commands/from_yaml.rs | 2 +- src/commands/open.rs | 20 +++----- src/commands/ps.rs | 2 +- src/commands/size.rs | 2 +- src/commands/sysinfo.rs | 2 +- src/commands/to_array.rs | 5 +- src/commands/trim.rs | 2 +- src/commands/where_.rs | 1 - src/context.rs | 14 +----- src/lib.rs | 2 + src/object.rs | 2 +- src/object/base.rs | 1 + src/parser/parse_command.rs | 10 ++-- src/parser/registry.rs | 42 +++++++++++++++-- src/prelude.rs | 3 +- src/shell/completer.rs | 1 - src/utils.rs | 62 +++++++++++++++++++++++++ tests/commands_test.rs | 91 +++++++++++++++++++++++++------------ tests/helpers/mod.rs | 49 ++++++++++++++------ 24 files changed, 253 insertions(+), 124 deletions(-) create mode 100644 src/utils.rs diff --git a/Cargo.lock b/Cargo.lock index 40b794e3ee..1f719cab7e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1705,6 +1705,7 @@ dependencies = [ "syntect 3.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "sys-info 0.5.7 (registry+https://github.com/rust-lang/crates.io-index)", "sysinfo 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", "tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "term 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", "toml 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2146,6 +2147,18 @@ dependencies = [ "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "rand" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "rand" version = "0.6.5" @@ -2829,6 +2842,15 @@ dependencies = [ "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "tempdir" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rand 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "remove_dir_all 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "tempfile" version = "3.1.0" @@ -3627,6 +3649,7 @@ dependencies = [ "checksum publicsuffix 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "5afecba86dcf1e4fd610246f89899d1924fe12e1e89f555eb7c7f710f3c5ad1d" "checksum quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9274b940887ce9addde99c4eee6b5c44cc494b182b97e73dc8ffdcb3397fd3f0" "checksum quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)" = "faf4799c5d274f3868a4aae320a0a182cbd2baee377b378f080e16a23e9d80db" +"checksum rand 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293" "checksum rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca" "checksum rand 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d47eab0e83d9693d40f825f86948aa16eff6750ead4bdffc4ab95b8b3a7f052c" "checksum rand_chacha 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef" @@ -3705,6 +3728,7 @@ dependencies = [ "checksum syntect 3.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e80b8831c5a543192ffc3727f01cf0e57579c6ac15558e3048bfb5708892167b" "checksum sys-info 0.5.7 (registry+https://github.com/rust-lang/crates.io-index)" = "76d6cf7b349b6a6daaf7a3797227e2f4108c8dd398e0aca7e29b9fb239948541" "checksum sysinfo 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c3e2cab189e59f72710e3dd5e1e0d5be0f6c5c999c326f2fdcdf3bf4483ec9fd" +"checksum tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "15f2b5fb00ccdf689e0149d1b1b3c03fead81c2b37735d812fa8bddbbf41b6d8" "checksum tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9" "checksum term 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "edd106a334b7657c10b7c540a0106114feadeb4dc314513e97df481d5d966f42" "checksum termcolor 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "96d6098003bde162e4277c70665bd87c326f5a0c3f3fbfb285787fa482d54e6e" diff --git a/Cargo.toml b/Cargo.toml index 08559b23c8..c7efe3f555 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -81,6 +81,7 @@ syntect = "3.2.0" [dev-dependencies] pretty_assertions = "0.6.1" +tempdir = "0.3.7" [lib] name = "nu" diff --git a/src/commands.rs b/src/commands.rs index 8d813191ce..9c60aabf16 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -42,7 +42,7 @@ crate mod trim; crate mod vtable; crate mod where_; -crate use command::{command, filter, EvaluatedFilterCommandArgs, EvaluatedStaticCommandArgs}; +crate use command::{command, EvaluatedStaticCommandArgs}; crate use config::Config; crate use open::Open; crate use rm::Remove; diff --git a/src/commands/command.rs b/src/commands/command.rs index 1e14fbfcea..d3cd6ba86b 100644 --- a/src/commands/command.rs +++ b/src/commands/command.rs @@ -27,18 +27,6 @@ impl ToDebug for UnevaluatedCallInfo { } impl UnevaluatedCallInfo { - fn name(&self) -> Result<&str, ShellError> { - let head = &self.args.head(); - match head.item() { - hir::RawExpression::Literal(hir::Literal::Bare) => Ok(head.span.slice(&self.source)), - hir::RawExpression::Literal(hir::Literal::String(span)) => Ok(span.slice(&self.source)), - other => Err(ShellError::type_error( - "Command name", - head.type_name().spanned(head.span), - )), - } - } - fn evaluate( self, registry: ®istry::CommandRegistry, @@ -94,20 +82,6 @@ impl CommandArgs { } } -pub enum EvaluatedInput { - Static(InputStream), - Filter(Spanned), -} - -impl EvaluatedInput { - pub fn stream(self) -> InputStream { - match self { - EvaluatedInput::Static(stream) => stream, - EvaluatedInput::Filter(value) => vec![value].into(), - } - } -} - pub struct EvaluatedStaticCommandArgs { pub args: EvaluatedCommandArgs, pub input: InputStream, @@ -152,6 +126,7 @@ impl EvaluatedStaticCommandArgs { #[get = "pub"] pub struct EvaluatedFilterCommandArgs { args: EvaluatedCommandArgs, + #[allow(unused)] input: Spanned, } @@ -189,8 +164,6 @@ pub struct EvaluatedCommandArgs { } impl EvaluatedCommandArgs { - pub fn parts(self) -> () {} - pub fn call_args(&self) -> ®istry::EvaluatedArgs { &self.call_info.args } @@ -199,10 +172,6 @@ impl EvaluatedCommandArgs { self.call_info.args.nth(pos) } - pub fn positional_iter(&self) -> impl Iterator> { - self.call_info.args.positional_iter() - } - pub fn expect_nth(&self, pos: usize) -> Result<&Spanned, ShellError> { self.call_info.args.expect_nth(pos) } @@ -308,6 +277,7 @@ pub trait Sink { } } +#[allow(unused)] pub struct FnFilterCommand { name: String, func: fn(EvaluatedFilterCommandArgs) -> Result, @@ -397,6 +367,7 @@ pub fn command( }) } +#[allow(unused)] pub fn filter( name: &str, func: fn(EvaluatedFilterCommandArgs) -> Result, diff --git a/src/commands/exit.rs b/src/commands/exit.rs index 8bba11b49b..9dabe99f84 100644 --- a/src/commands/exit.rs +++ b/src/commands/exit.rs @@ -2,6 +2,6 @@ use crate::commands::command::CommandAction; use crate::errors::ShellError; use crate::prelude::*; -pub fn exit(_args: CommandArgs, registry: &CommandRegistry) -> Result { +pub fn exit(_args: CommandArgs, _registry: &CommandRegistry) -> Result { Ok(vec![Ok(ReturnSuccess::Action(CommandAction::Exit))].into()) } diff --git a/src/commands/from_yaml.rs b/src/commands/from_yaml.rs index 69d42697df..eb38159df7 100644 --- a/src/commands/from_yaml.rs +++ b/src/commands/from_yaml.rs @@ -50,7 +50,7 @@ pub fn from_yaml_string_to_value( pub fn from_yaml( args: CommandArgs, - registry: &CommandRegistry, + _registry: &CommandRegistry, ) -> Result { let span = args.name_span(); let out = args.input; diff --git a/src/commands/open.rs b/src/commands/open.rs index d8d8a4a67a..172526e3e2 100644 --- a/src/commands/open.rs +++ b/src/commands/open.rs @@ -1,11 +1,10 @@ use crate::context::SpanSource; use crate::errors::ShellError; -use crate::object::{Primitive, Switch, Value}; +use crate::object::{Primitive, Value}; use crate::parser::hir::SyntaxType; use crate::parser::parse::span::Span; -use crate::parser::registry::{self, CommandConfig, NamedType, PositionalType}; +use crate::parser::registry::{self, CommandConfig, NamedType}; use crate::prelude::*; -use indexmap::IndexMap; use mime::Mime; use std::path::{Path, PathBuf}; use std::str::FromStr; @@ -77,17 +76,10 @@ impl Command for Open { } fn config(&self) -> CommandConfig { - let mut named = IndexMap::default(); - named.insert("raw".to_string(), NamedType::Switch); - - CommandConfig { - name: self.name().to_string(), - positional: vec![PositionalType::mandatory("path", SyntaxType::Block)], - rest_positional: false, - named, - is_sink: true, - is_filter: false, - } + CommandConfig::new(self.name()) + .required("path", SyntaxType::Block) + .named("raw", NamedType::Switch) + .sink() } } diff --git a/src/commands/ps.rs b/src/commands/ps.rs index 8abaa15b56..ed1479007f 100644 --- a/src/commands/ps.rs +++ b/src/commands/ps.rs @@ -3,7 +3,7 @@ use crate::object::process::process_dict; use crate::prelude::*; use sysinfo::{RefreshKind, SystemExt}; -pub fn ps(args: CommandArgs, registry: &CommandRegistry) -> Result { +pub fn ps(args: CommandArgs, _registry: &CommandRegistry) -> Result { let mut system = sysinfo::System::new_with_specifics(RefreshKind::new().with_processes()); system.refresh_processes(); let list = system.get_process_list(); diff --git a/src/commands/size.rs b/src/commands/size.rs index 1ed87effc0..9dc497aea8 100644 --- a/src/commands/size.rs +++ b/src/commands/size.rs @@ -2,7 +2,7 @@ use crate::errors::ShellError; use crate::object::{SpannedDictBuilder, Value}; use crate::prelude::*; -pub fn size(args: CommandArgs, registry: &CommandRegistry) -> Result { +pub fn size(args: CommandArgs, _registry: &CommandRegistry) -> Result { let input = args.input; Ok(input .values diff --git a/src/commands/sysinfo.rs b/src/commands/sysinfo.rs index d0eb2f69c9..c2296a0bdc 100644 --- a/src/commands/sysinfo.rs +++ b/src/commands/sysinfo.rs @@ -6,7 +6,7 @@ use crate::prelude::*; use sys_info::*; use sysinfo::{ComponentExt, DiskExt, NetworkExt, RefreshKind, SystemExt}; -pub fn sysinfo(args: CommandArgs, registry: &CommandRegistry) -> Result { +pub fn sysinfo(args: CommandArgs, _registry: &CommandRegistry) -> Result { let name_span = args.name_span(); let mut idx = SpannedDictBuilder::new(name_span); diff --git a/src/commands/to_array.rs b/src/commands/to_array.rs index d5fcfa3153..8cd7acc4ef 100644 --- a/src/commands/to_array.rs +++ b/src/commands/to_array.rs @@ -1,7 +1,10 @@ use crate::object::Value; use crate::prelude::*; -pub fn to_array(args: CommandArgs, registry: &CommandRegistry) -> Result { +pub fn to_array( + args: CommandArgs, + _registry: &CommandRegistry, +) -> Result { let out = args.input.values.collect(); Ok(out diff --git a/src/commands/trim.rs b/src/commands/trim.rs index 5224a23923..bede0e1b1d 100644 --- a/src/commands/trim.rs +++ b/src/commands/trim.rs @@ -2,7 +2,7 @@ use crate::errors::ShellError; use crate::object::Value; use crate::prelude::*; -pub fn trim(args: CommandArgs, registry: &CommandRegistry) -> Result { +pub fn trim(args: CommandArgs, _registry: &CommandRegistry) -> Result { let input = args.input; Ok(input diff --git a/src/commands/where_.rs b/src/commands/where_.rs index f852b46a54..d9480aa4ed 100644 --- a/src/commands/where_.rs +++ b/src/commands/where_.rs @@ -1,6 +1,5 @@ use crate::errors::ShellError; use crate::object::base as value; -use crate::object::{types, Value}; use crate::parser::hir::SyntaxType; use crate::parser::registry::{self, CommandConfig, PositionalType}; use crate::prelude::*; diff --git a/src/context.rs b/src/context.rs index 2f9d00c98f..1eed5d39b7 100644 --- a/src/context.rs +++ b/src/context.rs @@ -1,9 +1,5 @@ use crate::commands::command::{CallInfo, Sink, SinkCommandArgs, UnevaluatedCallInfo}; -use crate::parser::{ - hir, - registry::{self, CommandConfig}, - Span, -}; +use crate::parser::{hir, registry, Span}; use crate::prelude::*; use derive_new::new; @@ -51,12 +47,6 @@ impl CommandRegistry { } } - fn get_config(&self, name: &str) -> Option { - let registry = self.registry.lock().unwrap(); - - registry.get(name).map(|c| c.config()) - } - fn get_command(&self, name: &str) -> Option> { let registry = self.registry.lock().unwrap(); @@ -75,7 +65,7 @@ impl CommandRegistry { } crate fn names(&self) -> Vec { - let mut registry = self.registry.lock().unwrap(); + let registry = self.registry.lock().unwrap(); registry.keys().cloned().collect() } } diff --git a/src/lib.rs b/src/lib.rs index 15e3bf3ef1..813de129b5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -24,6 +24,7 @@ mod plugin; mod shell; mod stream; mod traits; +mod utils; pub use crate::commands::command::{CallInfo, ReturnSuccess, ReturnValue}; pub use crate::context::SpanSource; @@ -31,6 +32,7 @@ pub use crate::env::host::BasicHost; pub use crate::parser::parse::span::SpannedItem; pub use crate::parser::Spanned; pub use crate::plugin::{serve_plugin, Plugin}; +pub use crate::utils::{AbsolutePath, RelativePath}; pub use cli::cli; pub use errors::ShellError; pub use object::base::{Primitive, Value}; diff --git a/src/object.rs b/src/object.rs index f094c63a17..08f660fdae 100644 --- a/src/object.rs +++ b/src/object.rs @@ -6,6 +6,6 @@ crate mod into; crate mod process; crate mod types; -crate use base::{Block, Primitive, Switch, Value}; +crate use base::{Primitive, Value}; crate use dict::{Dictionary, SpannedDictBuilder}; crate use files::dir_entry_dict; diff --git a/src/object/base.rs b/src/object/base.rs index 6542ecaac2..1a49f97a11 100644 --- a/src/object/base.rs +++ b/src/object/base.rs @@ -266,6 +266,7 @@ pub enum Switch { } impl Switch { + #[allow(unused)] pub fn is_present(&self) -> bool { match self { Switch::Present => true, diff --git a/src/parser/parse_command.rs b/src/parser/parse_command.rs index a5a2b0fa4f..ec4702fc99 100644 --- a/src/parser/parse_command.rs +++ b/src/parser/parse_command.rs @@ -77,7 +77,7 @@ fn parse_command_tail( trace_remaining("nodes", tail.clone(), source); - for (name, kind) in config.named() { + for (name, kind) in &config.named { trace!(target: "nu::parse", "looking for {} : {:?}", name, kind); match kind { @@ -115,7 +115,7 @@ fn parse_command_tail( if tail.at_end() { return Err(ShellError::argument_error( - config.name().clone(), + config.name.clone(), ArgumentError::MissingValueForName(name.to_string()), flag.span, )); @@ -139,14 +139,14 @@ fn parse_command_tail( let mut positional = vec![]; - for arg in config.positional() { + for arg in &config.positional { trace!("Processing positional {:?}", arg); match arg { PositionalType::Mandatory(..) => { if tail.len() == 0 { return Err(ShellError::argument_error( - config.name().clone(), + config.name.clone(), ArgumentError::MissingMandatoryPositional(arg.name().to_string()), command_span, )); @@ -207,7 +207,7 @@ fn extract_mandatory( match flag { None => Err(ShellError::argument_error( - config.name().clone(), + config.name.clone(), ArgumentError::MissingMandatoryFlag(name.to_string()), span, )), diff --git a/src/parser/registry.rs b/src/parser/registry.rs index a0d0bf221b..159d99193d 100644 --- a/src/parser/registry.rs +++ b/src/parser/registry.rs @@ -4,7 +4,6 @@ use crate::evaluate::{evaluate_baseline_expr, Scope}; use crate::parser::{hir, hir::SyntaxType, parse_command, CallNode, Spanned}; use crate::prelude::*; use derive_new::new; -use getset::Getters; use indexmap::IndexMap; use log::trace; use serde::{Deserialize, Serialize}; @@ -70,8 +69,7 @@ impl PositionalType { } } -#[derive(Debug, Getters, Serialize, Deserialize, Clone)] -#[get = "crate"] +#[derive(Debug, Serialize, Deserialize, Clone)] pub struct CommandConfig { pub name: String, pub positional: Vec, @@ -81,6 +79,44 @@ pub struct CommandConfig { pub is_sink: bool, } +impl CommandConfig { + pub fn new(name: impl Into) -> CommandConfig { + CommandConfig { + name: name.into(), + positional: vec![], + rest_positional: false, + named: IndexMap::default(), + is_filter: false, + is_sink: false, + } + } + + pub fn required(mut self, name: impl Into, ty: impl Into) -> CommandConfig { + self.positional + .push(PositionalType::Mandatory(name.into(), ty.into())); + + self + } + + pub fn optional(mut self, name: impl Into, ty: impl Into) -> CommandConfig { + self.positional + .push(PositionalType::Optional(name.into(), ty.into())); + + self + } + + pub fn named(mut self, name: impl Into, ty: impl Into) -> CommandConfig { + self.named.insert(name.into(), ty.into()); + + self + } + + pub fn sink(mut self) -> CommandConfig { + self.is_sink = true; + self + } +} + #[derive(Debug, Default, new, Serialize, Deserialize)] pub struct EvaluatedArgs { pub positional: Option>>, diff --git a/src/prelude.rs b/src/prelude.rs index b6f3f5f112..8b112c8817 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -34,8 +34,7 @@ macro_rules! trace_stream { crate use crate::cli::MaybeOwned; crate use crate::commands::command::{ - Command, CommandAction, CommandArgs, EvaluatedCommandArgs, ReturnSuccess, ReturnValue, Sink, - SinkCommandArgs, + Command, CommandAction, CommandArgs, ReturnSuccess, ReturnValue, Sink, SinkCommandArgs, }; crate use crate::context::{CommandRegistry, Context}; crate use crate::env::host::handle_unexpected; diff --git a/src/shell/completer.rs b/src/shell/completer.rs index af1276e835..c2bb2c9228 100644 --- a/src/shell/completer.rs +++ b/src/shell/completer.rs @@ -1,5 +1,4 @@ use crate::context::CommandRegistry; -use crate::prelude::*; use derive_new::new; use rustyline::completion::Completer; diff --git a/src/utils.rs b/src/utils.rs new file mode 100644 index 0000000000..7915a35a16 --- /dev/null +++ b/src/utils.rs @@ -0,0 +1,62 @@ +use std::ops::Div; +use std::path::{Path, PathBuf}; + +pub struct AbsolutePath { + inner: PathBuf, +} + +impl AbsolutePath { + pub fn new(path: impl AsRef) -> AbsolutePath { + let path = path.as_ref(); + + if path.is_absolute() { + AbsolutePath { + inner: path.to_path_buf(), + } + } else { + panic!("AbsolutePath::new must take an absolute path") + } + } +} + +impl Div<&str> for AbsolutePath { + type Output = AbsolutePath; + + fn div(self, rhs: &str) -> Self::Output { + AbsolutePath { + inner: self.inner.join(rhs), + } + } +} + +impl AsRef for AbsolutePath { + fn as_ref(&self) -> &Path { + self.inner.as_path() + } +} + +pub struct RelativePath { + inner: PathBuf, +} + +impl RelativePath { + pub fn new(path: impl Into) -> RelativePath { + let path = path.into(); + + if path.is_relative() { + RelativePath { inner: path } + } else { + panic!("RelativePath::new must take a relative path") + } + } +} + +impl> Div for RelativePath { + type Output = RelativePath; + + fn div(self, rhs: T) -> Self::Output { + RelativePath { + inner: self.inner.join(rhs.as_ref()), + } + } +} diff --git a/tests/commands_test.rs b/tests/commands_test.rs index dd9863e05b..997bc7956f 100644 --- a/tests/commands_test.rs +++ b/tests/commands_test.rs @@ -3,6 +3,8 @@ mod helpers; use h::in_directory as cwd; use helpers as h; +use nu::AbsolutePath; + #[test] fn lines() { nu!(output, @@ -80,73 +82,102 @@ fn open_error_if_file_not_found() { } #[test] -fn save_can_write_out_csv() { - let (playground_path, tests_dir) = h::setup_playground_for("save_test"); +fn save_can_write_out_csv() -> Result<(), std::io::Error> { + let (playground, tmp, dir) = h::setup_playground_for("save_test")?; - let full_path = format!("{}/{}", playground_path, tests_dir ); - let expected_file = format!("{}/{}", full_path , "cargo_sample.csv"); + let tmp = AbsolutePath::new(tmp); - nu!( - _output, - cwd(&playground_path), - "open ../formats/cargo_sample.toml | inc package.version --minor | get package | save save_test/cargo_sample.csv" + let expected_file = tmp / "cargo_sample.csv"; + + let root = AbsolutePath::new(std::env::current_dir()?); + + let path = root / "tests" / "fixtures" / "formats" / "cargo_sample.toml"; + + let command = format!( + "open {} | inc package.version --minor | get package | save {}/cargo_sample.csv", + path.as_ref().display(), + dir ); + nu!(_output, playground.path().display(), command); + let actual = h::file_contents(&expected_file); assert!(actual.contains("[list list],A shell for the GitHub era,2018,ISC,nu,0.2.0")); + + Ok(()) } #[test] -fn rm_can_remove_a_file() { - let directory = "tests/fixtures/nuplayground"; - let file = format!("{}/rm_test.txt", directory); +fn rm_can_remove_a_file() -> Result<(), std::io::Error> { + let _ = pretty_env_logger::try_init(); - h::create_file_at(&file); + let (_playground, tmp, _) = h::setup_playground_for("remove_file")?; - nu!(_output, cwd(directory), "rm rm_test.txt"); + let file = AbsolutePath::new(&tmp) / "rm_test.txt"; + // let file = tmp.path().join("rm_test.txt"); - assert!(!h::file_exists_at(&file)); + h::create_file_at(&file)?; + + nu!(_output, tmp.path().display(), "rm rm_test.txt"); + + assert!(!file.as_ref().exists()); + + Ok(()) } #[test] -fn rm_can_remove_directory_contents_with_recursive_flag() { - let (playground_path, tests_dir) = h::setup_playground_for("rm_test"); +fn rm_can_remove_directory_contents_with_recursive_flag() -> Result<(), std::io::Error> { + let _ = pretty_env_logger::try_init(); + + let (playground, tmp, dir) = h::setup_playground_for("rm_test")?; for f in ["yehuda.txt", "jonathan.txt", "andres.txt"].iter() { - h::create_file_at(&format!("{}/{}/{}", playground_path, tests_dir, f)); + h::create_file_at(&tmp.path().join(f))?; } nu!( _output, - cwd("tests/fixtures/nuplayground"), - "rm rm_test --recursive" + playground.path().display(), + format!("rm {} --recursive", dir) ); - assert!(!h::file_exists_at(&format!("{}/{}", playground_path, tests_dir))); + assert!(!tmp.path().exists()); + + Ok(()) } #[test] -fn rm_error_if_attempting_to_delete_a_directory_without_recursive_flag() { - let (playground_path, tests_dir) = h::setup_playground_for("rm_test_2"); - let full_path = format!("{}/{}", playground_path, tests_dir); +fn rm_error_if_attempting_to_delete_a_directory_without_recursive_flag( +) -> Result<(), std::io::Error> { + let (playground, tmp, dir) = h::setup_playground_for("rm_test_2")?; - nu_error!(output, cwd("tests/fixtures/nuplayground"), "rm rm_test_2"); + nu_error!(output, playground.path().display(), format!("rm {}", dir)); - assert!(h::file_exists_at(&full_path)); + assert!(tmp.path().exists()); assert!(output.contains("is a directory")); - h::delete_directory_at(&full_path); + h::delete_directory_at(tmp.path()); + + Ok(()) } #[test] -fn rm_error_if_attempting_to_delete_single_dot_as_argument() { - nu_error!(output, cwd("tests/fixtures/nuplayground"), "rm ."); +fn rm_error_if_attempting_to_delete_single_dot_as_argument() -> Result<(), std::io::Error> { + let (_playground, tmp, _) = h::setup_playground_for("rm_test_2")?; + + nu_error!(output, tmp.path().display(), "rm ."); assert!(output.contains("may not be removed")); + + Ok(()) } #[test] -fn rm_error_if_attempting_to_delete_two_dot_as_argument() { - nu_error!(output, cwd("tests/fixtures/nuplayground"), "rm .."); +fn rm_error_if_attempting_to_delete_two_dot_as_argument() -> Result<(), std::io::Error> { + let (_playground, tmp, _) = h::setup_playground_for("rm_test_2")?; + + nu_error!(output, tmp.path().display(), "rm .."); assert!(output.contains("may not be removed")); + + Ok(()) } diff --git a/tests/helpers/mod.rs b/tests/helpers/mod.rs index bd9a3106e7..81e4313b94 100644 --- a/tests/helpers/mod.rs +++ b/tests/helpers/mod.rs @@ -1,8 +1,10 @@ #![allow(dead_code)] -pub use std::path::PathBuf; +pub use std::path::{Path, PathBuf}; +use log::trace; use std::io::Read; +use tempdir::TempDir; #[macro_export] macro_rules! nu { @@ -79,20 +81,30 @@ macro_rules! nu_error { }; } -pub fn setup_playground_for(topic: &str) -> (String, String) { - let home = "tests/fixtures/nuplayground"; - let full_path = format!("{}/{}", home, topic); +pub fn setup_playground_for(topic: &str) -> Result<(TempDir, TempDir, String), std::io::Error> { + let home = TempDir::new("nuplayground")?; + let child = TempDir::new_in(home.path(), topic)?; + let relative = child + .path() + .file_name() + .unwrap() + .to_str() + .expect(&format!( + "file name {} was not valid", + child.path().display() + )) + .to_string(); - if file_exists_at(&full_path) { - delete_directory_at(&full_path); - } + trace!( + "created {:?} dir={}", + child.path().display(), + child.path().is_dir() + ); - create_directory_at(&full_path); - - (home.to_string(), topic.to_string()) + Ok((home, child, relative)) } -pub fn file_contents(full_path: &str) -> String { +pub fn file_contents(full_path: impl AsRef) -> String { let mut file = std::fs::File::open(full_path).expect("can not open file"); let mut contents = String::new(); file.read_to_string(&mut contents) @@ -100,19 +112,26 @@ pub fn file_contents(full_path: &str) -> String { contents } -pub fn create_file_at(full_path: &str) { - std::fs::write(PathBuf::from(full_path), "fake data".as_bytes()).expect("can not create file"); +pub fn create_file_at(full_path: impl AsRef) -> Result<(), std::io::Error> { + let full_path = full_path.as_ref(); + + assert!( + full_path.parent().unwrap().is_dir(), + "{:?} exists", + full_path.parent().unwrap().display(), + ); + std::fs::write(full_path, "fake data".as_bytes()) } pub fn file_exists_at(full_path: &str) -> bool { PathBuf::from(full_path).exists() } -pub fn delete_directory_at(full_path: &str) { +pub fn delete_directory_at(full_path: &Path) { std::fs::remove_dir_all(PathBuf::from(full_path)).expect("can not remove directory"); } -pub fn create_directory_at(full_path: &str) { +pub fn create_directory_at(full_path: &Path) { let path = PathBuf::from(full_path); println!("{:?} - is_dir: {:?}", path, path.is_dir()); From fc173c46d8a2b3b312a029f67aa2ad42c6466a28 Mon Sep 17 00:00:00 2001 From: Yehuda Katz Date: Fri, 2 Aug 2019 12:15:07 -0700 Subject: [PATCH 03/12] Restructuring --- Cargo.lock | 171 ++++++++----- Cargo.toml | 11 +- src/cli.rs | 96 +++----- src/commands.rs | 9 +- src/commands/autoview.rs | 26 +- src/commands/cd.rs | 49 +++- src/commands/classified.rs | 45 +--- src/commands/clip.rs | 62 ++++- src/commands/command.rs | 196 +++++++++------ src/commands/config.rs | 117 ++++----- src/commands/from_toml.rs | 2 +- src/commands/get.rs | 67 +++--- src/commands/macros.rs | 32 +-- src/commands/open.rs | 117 ++++----- src/commands/plugin.rs | 54 +++-- src/commands/rm.rs | 64 +++-- src/commands/save.rs | 197 ++++++++------- src/commands/skip_while.rs | 56 ++--- src/commands/table.rs | 3 +- src/commands/vtable.rs | 3 +- src/commands/where_.rs | 95 +++----- src/context.rs | 54 +---- src/errors.rs | 19 +- src/evaluate/evaluator.rs | 10 + src/lib.rs | 6 +- src/object.rs | 2 +- src/object/base.rs | 34 +-- src/object/config.rs | 34 ++- src/object/types.rs | 215 ++++++++++++----- src/parser.rs | 2 + src/parser/deserializer.rs | 307 ++++++++++++++++++++++++ src/parser/hir.rs | 13 +- src/parser/hir/baseline_parse_tokens.rs | 12 +- src/parser/hir/binary.rs | 2 +- src/parser/hir/named.rs | 2 +- src/parser/hir/path.rs | 2 +- src/parser/parse/flag.rs | 2 +- src/parser/parse/operator.rs | 2 +- src/parser/parse/span.rs | 2 +- src/parser/parse/unit.rs | 6 +- src/parser/parse_command.rs | 8 +- src/parser/registry.rs | 62 +++-- src/plugin.rs | 4 +- src/plugins/add.rs | 6 +- src/plugins/binaryview.rs | 6 +- src/plugins/edit.rs | 6 +- src/plugins/inc.rs | 6 +- src/plugins/skip.rs | 6 +- src/plugins/tree.rs | 6 +- src/prelude.rs | 7 +- src/stream.rs | 7 +- src/utils.rs | 22 +- tests/commands_test.rs | 26 +- tests/helpers/mod.rs | 7 +- 54 files changed, 1498 insertions(+), 879 deletions(-) create mode 100644 src/parser/deserializer.rs diff --git a/Cargo.lock b/Cargo.lock index 1f719cab7e..27f3b6a135 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -9,7 +9,7 @@ dependencies = [ "quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", "regex 1.1.9 (registry+https://github.com/rust-lang/crates.io-index)", "regex-syntax 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.37 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.42 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -77,6 +77,16 @@ dependencies = [ "nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "async-trait" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.42 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "atty" version = "0.2.11" @@ -570,7 +580,7 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.37 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.42 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -609,7 +619,7 @@ dependencies = [ "ident_case 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", "quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.37 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.42 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -622,7 +632,7 @@ dependencies = [ "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", "quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", "strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.37 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.42 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -632,7 +642,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "darling_core 0.8.6 (registry+https://github.com/rust-lang/crates.io-index)", "quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.37 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.42 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -642,7 +652,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "darling_core 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.37 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.42 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -661,7 +671,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", "quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.37 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.42 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -673,7 +683,7 @@ dependencies = [ "derive_builder_core 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", "quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.37 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.42 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -684,7 +694,7 @@ dependencies = [ "darling 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", "quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.37 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.42 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -697,7 +707,7 @@ dependencies = [ "quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", "regex 1.1.9 (registry+https://github.com/rust-lang/crates.io-index)", "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.37 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.42 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -844,7 +854,7 @@ dependencies = [ "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", "quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive_internals 0.24.1 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.37 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.42 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -898,7 +908,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", "quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.37 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.42 (registry+https://github.com/rust-lang/crates.io-index)", "synstructure 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -961,16 +971,37 @@ version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] -name = "futures-channel-preview" -version = "0.3.0-alpha.16" +name = "futures-async-stream" +version = "0.1.0-alpha.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "futures-core-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-async-stream-macro 0.1.0-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-core-preview 0.3.0-alpha.17 (registry+https://github.com/rust-lang/crates.io-index)", + "pin-project 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "futures-async-stream-macro" +version = "0.1.0-alpha.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.42 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "futures-channel-preview" +version = "0.3.0-alpha.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "futures-core-preview 0.3.0-alpha.17 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-sink-preview 0.3.0-alpha.17 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "futures-core-preview" -version = "0.3.0-alpha.16" +version = "0.3.0-alpha.17" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -984,56 +1015,51 @@ dependencies = [ [[package]] name = "futures-executor-preview" -version = "0.3.0-alpha.16" +version = "0.3.0-alpha.17" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "futures-channel-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-core-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-util-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-channel-preview 0.3.0-alpha.17 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-core-preview 0.3.0-alpha.17 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-util-preview 0.3.0-alpha.17 (registry+https://github.com/rust-lang/crates.io-index)", "num_cpus 1.10.1 (registry+https://github.com/rust-lang/crates.io-index)", - "pin-utils 0.1.0-alpha.4 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "futures-io-preview" -version = "0.3.0-alpha.16" +version = "0.3.0-alpha.17" source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "futures-core-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)", -] [[package]] name = "futures-preview" -version = "0.3.0-alpha.16" +version = "0.3.0-alpha.17" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "futures-channel-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-core-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-executor-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-io-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-sink-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-util-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-channel-preview 0.3.0-alpha.17 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-core-preview 0.3.0-alpha.17 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-executor-preview 0.3.0-alpha.17 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-io-preview 0.3.0-alpha.17 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-sink-preview 0.3.0-alpha.17 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-util-preview 0.3.0-alpha.17 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "futures-sink-preview" -version = "0.3.0-alpha.16" +version = "0.3.0-alpha.17" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "futures-channel-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-core-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-core-preview 0.3.0-alpha.17 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "futures-util-preview" -version = "0.3.0-alpha.16" +version = "0.3.0-alpha.17" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-channel-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-core-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-io-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-sink-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-channel-preview 0.3.0-alpha.17 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-core-preview 0.3.0-alpha.17 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-io-preview 0.3.0-alpha.17 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-sink-preview 0.3.0-alpha.17 (registry+https://github.com/rust-lang/crates.io-index)", "memchr 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "pin-utils 0.1.0-alpha.4 (registry+https://github.com/rust-lang/crates.io-index)", "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1042,11 +1068,11 @@ dependencies = [ [[package]] name = "futures_codec" -version = "0.2.2" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-preview 0.3.0-alpha.17 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1065,7 +1091,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", "quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.37 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.42 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1445,7 +1471,7 @@ dependencies = [ "quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", "regex-syntax 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-hash 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.37 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.42 (registry+https://github.com/rust-lang/crates.io-index)", "utf8-ranges 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1648,6 +1674,7 @@ dependencies = [ "adhoc_derive 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "ansi_term 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", "app_dirs 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "async-trait 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "byte-unit 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", "chrono 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1664,9 +1691,10 @@ dependencies = [ "dunce 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "enum-utils 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "enum_derive 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-sink-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)", - "futures_codec 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-async-stream 0.1.0-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-preview 0.3.0-alpha.17 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-sink-preview 0.3.0-alpha.17 (registry+https://github.com/rust-lang/crates.io-index)", + "futures_codec 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", "getset 0.0.7 (registry+https://github.com/rust-lang/crates.io-index)", "git2 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", "image 0.21.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1697,7 +1725,6 @@ dependencies = [ "serde 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)", "serde-hjson 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde_bytes 0.11.1 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)", "serde_ini 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)", "serde_yaml 0.8.9 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1721,7 +1748,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", "quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.37 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.42 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2000,6 +2027,16 @@ dependencies = [ "unicase 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "pin-project" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.42 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "pin-utils" version = "0.1.0-alpha.4" @@ -2636,7 +2673,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", "quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.37 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.42 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2645,7 +2682,7 @@ version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.37 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.42 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2781,7 +2818,7 @@ dependencies = [ [[package]] name = "syn" -version = "0.15.37" +version = "0.15.42" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2796,7 +2833,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", "quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.37 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.42 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -3115,7 +3152,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "darling 0.8.6 (registry+https://github.com/rust-lang/crates.io-index)", "quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.37 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.42 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -3287,7 +3324,7 @@ dependencies = [ "log 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", "quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.37 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.42 (registry+https://github.com/rust-lang/crates.io-index)", "wasm-bindgen-shared 0.2.47 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -3307,7 +3344,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", "quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.37 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.42 (registry+https://github.com/rust-lang/crates.io-index)", "wasm-bindgen-backend 0.2.47 (registry+https://github.com/rust-lang/crates.io-index)", "wasm-bindgen-shared 0.2.47 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -3431,6 +3468,7 @@ dependencies = [ "checksum app_dirs 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e73a24bad9bd6a94d6395382a6c69fe071708ae4409f763c5475e14ee896313d" "checksum argon2rs 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "3f67b0b6a86dae6e67ff4ca2b6201396074996379fba2b92ff649126f37cb392" "checksum arrayvec 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)" = "92c7fb76bc8826a8b33b4ee5bb07a247a81e76764ab4d55e8f73e3a4d8808c71" +"checksum async-trait 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "41f6b40ac5df0ab7ef35112d7a593428ca62bcaabcc7de20f88b506cf798928d" "checksum atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "9a7d5b8723950951411ee34d271d99dddcc2035a16ab25310ea2c8cfd4369652" "checksum autocfg 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "0e49efa51329a5fd37e7c79db4621af617cd4e3e5bc224939808d076077077bf" "checksum backtrace 0.3.30 (registry+https://github.com/rust-lang/crates.io-index)" = "ada4c783bb7e7443c14e0480f429ae2cc99da95065aeab7ee1b81ada0419404f" @@ -3532,15 +3570,17 @@ dependencies = [ "checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" "checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" "checksum futures 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)" = "a2037ec1c6c1c4f79557762eab1f7eae1f64f6cb418ace90fae88f0942b60139" -"checksum futures-channel-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)" = "4cd523712fc272e9b714669165a2832debee5a5b7e409bfccdc7c0d5cd0cf07a" -"checksum futures-core-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)" = "719770f328642b657b849856bb5a607db9538dd5bb3000122e5ead55d0a58c36" +"checksum futures-async-stream 0.1.0-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c4aecac975d38ff13b935de313e12fded407231b0d563e7493fac40463272a2f" +"checksum futures-async-stream-macro 0.1.0-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4bbd6c677284a4bb3c043193ad20017f06c61cca34327ce5b0682691f92a182e" +"checksum futures-channel-preview 0.3.0-alpha.17 (registry+https://github.com/rust-lang/crates.io-index)" = "21c71ed547606de08e9ae744bb3c6d80f5627527ef31ecf2a7210d0e67bc8fae" +"checksum futures-core-preview 0.3.0-alpha.17 (registry+https://github.com/rust-lang/crates.io-index)" = "4b141ccf9b7601ef987f36f1c0d9522f76df3bba1cf2e63bfacccc044c4558f5" "checksum futures-cpupool 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "ab90cde24b3319636588d0c35fe03b1333857621051837ed769faefb4c2162e4" -"checksum futures-executor-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)" = "315dc58c908535d059576a329b86cd185933433382cfcd394fb2fa353330de03" -"checksum futures-io-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)" = "cca0bf7a1f39c9d32b797b0def93d5932aa71796236aad6b549bac6f7df159a3" -"checksum futures-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)" = "fcfeac5f016a4b5835bb93eb7961f50a64f0e001207562703d9ddf4109d7b263" -"checksum futures-sink-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)" = "49dcfdacd6b5974ca0b9b78bc38ffd1071da0206179735c3df82e279f5b784e4" -"checksum futures-util-preview 0.3.0-alpha.16 (registry+https://github.com/rust-lang/crates.io-index)" = "f7a0451b9c5047c2b9ab93425ffd0793165511e93c04b977cd45fbd41c6e34b2" -"checksum futures_codec 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b60f48aa03e365df015d2fbf0b79f17b440350c268a5e20305da17b394adcc1e" +"checksum futures-executor-preview 0.3.0-alpha.17 (registry+https://github.com/rust-lang/crates.io-index)" = "87ba260fe51080ba37f063ad5b0732c4ff1f737ea18dcb67833d282cdc2c6f14" +"checksum futures-io-preview 0.3.0-alpha.17 (registry+https://github.com/rust-lang/crates.io-index)" = "082e402605fcb8b1ae1e5ba7d7fdfd3e31ef510e2a8367dd92927bb41ae41b3a" +"checksum futures-preview 0.3.0-alpha.17 (registry+https://github.com/rust-lang/crates.io-index)" = "bf25f91c8a9a1f64c451e91b43ba269ed359b9f52d35ed4b3ce3f9c842435867" +"checksum futures-sink-preview 0.3.0-alpha.17 (registry+https://github.com/rust-lang/crates.io-index)" = "4309a25a1069a1f3c10647b227b9afe6722b67a030d3f00a9cbdc171fc038de4" +"checksum futures-util-preview 0.3.0-alpha.17 (registry+https://github.com/rust-lang/crates.io-index)" = "af8198c48b222f02326940ce2b3aa9e6e91a32886eeaad7ca3b8e4c70daa3f4e" +"checksum futures_codec 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "36552cd31353fd135114510d53b8d120758120c36aa636a9341970f9efb1e4a0" "checksum getrandom 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "e65cce4e5084b14874c4e7097f38cab54f47ee554f9194673456ea379dcc4c55" "checksum getset 0.0.7 (registry+https://github.com/rust-lang/crates.io-index)" = "19fbde0fad0c1c1f9474694b1f5c9ba22b09f2f74f74e6d2bd19c43f6656e2cb" "checksum gif 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)" = "86c2f2b597d6e05c86ee5947b2223bda468fe8dad3e88e2a6520869322aaf568" @@ -3634,6 +3674,7 @@ dependencies = [ "checksum phf_codegen 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)" = "b03e85129e324ad4166b06b2c7491ae27fe3ec353af72e72cd1654c7225d517e" "checksum phf_generator 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)" = "09364cc93c159b8b06b1f4dd8a4398984503483891b0c26b867cf431fb132662" "checksum phf_shared 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)" = "234f71a15de2288bcb7e3b6515828d22af7ec8598ee6d24c3b526fa0a80b67a0" +"checksum pin-project 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "2714268752963de91be73ea2689c3c63d2389b14fad04d033923e20cb3a1d99a" "checksum pin-utils 0.1.0-alpha.4 (registry+https://github.com/rust-lang/crates.io-index)" = "5894c618ce612a3fa23881b152b608bafb8c56cfc22f434a3ba3120b40f7b587" "checksum pkg-config 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)" = "676e8eb2b1b4c9043511a9b7bea0915320d7e502b0a079fb03f9635a5252b18c" "checksum plist 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "5f2a9f075f6394100e7c105ed1af73fb1859d6fd14e49d4290d578120beb167f" @@ -3723,7 +3764,7 @@ dependencies = [ "checksum strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bb4f380125926a99e52bc279241539c018323fab05ad6368b56f93d9369ff550" "checksum strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" "checksum subprocess 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)" = "28fc0f40f0c0da73339d347aa7d6d2b90341a95683a47722bc4eebed71ff3c00" -"checksum syn 0.15.37 (registry+https://github.com/rust-lang/crates.io-index)" = "e11410033fd5cf69a1cf2084604e011190c56f11e08ffc53df880f5f65f1c6e4" +"checksum syn 0.15.42 (registry+https://github.com/rust-lang/crates.io-index)" = "eadc09306ca51a40555dd6fc2b415538e9e18bc9f870e47b1a524a79fe2dcf5e" "checksum synstructure 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)" = "02353edf96d6e4dc81aea2d8490a7e9db177bf8acb0e951c24940bf866cb313f" "checksum syntect 3.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e80b8831c5a543192ffc3727f01cf0e57579c6ac15558e3048bfb5708892167b" "checksum sys-info 0.5.7 (registry+https://github.com/rust-lang/crates.io-index)" = "76d6cf7b349b6a6daaf7a3797227e2f4108c8dd398e0aca7e29b9fb239948541" diff --git a/Cargo.toml b/Cargo.toml index c7efe3f555..c5b7a5d328 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,18 +28,19 @@ chrono-humanize = "0.0.11" byte-unit = "2.1.0" ordered-float = {version = "1.0.2", features = ["serde"]} prettyprint = "0.7.0" -futures-preview = { version = "=0.3.0-alpha.16", features = ["compat", "io-compat"] } -futures-sink-preview = "=0.3.0-alpha.16" -futures_codec = "0.2.2" +futures-preview = { version = "=0.3.0-alpha.17", features = ["compat", "io-compat"] } +futures-sink-preview = "=0.3.0-alpha.17" +futures-async-stream = "0.1.0-alpha.1" +async-trait = "" +futures_codec = "0.2.5" term = "0.5.2" bytes = "0.4.12" log = "0.4.7" pretty_env_logger = "0.3.0" -serde = "1.0.94" +serde = { version = "1.0.94", features = ["derive"] } serde_json = "1.0.40" serde-hjson = "0.9.0" serde_yaml = "0.8" -serde_derive = "1.0.94" serde_bytes = "0.11.1" getset = "0.0.7" logos = "0.10.0-rc2" diff --git a/src/cli.rs b/src/cli.rs index a568b9e29f..02dd5de13b 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -1,18 +1,17 @@ use crate::commands::autoview; -use crate::commands::classified::SinkCommand; use crate::commands::classified::{ ClassifiedCommand, ClassifiedInputStream, ClassifiedPipeline, ExternalCommand, InternalCommand, StreamNext, }; -use crate::commands::command::sink; use crate::commands::plugin::JsonRpc; -use crate::commands::plugin::{PluginCommand, PluginSink}; +use crate::commands::plugin::PluginCommand; +use crate::commands::{static_command, Command}; use crate::context::Context; crate use crate::errors::ShellError; use crate::git::current_branch; use crate::object::Value; use crate::parser::parse::span::Spanned; -use crate::parser::registry::CommandConfig; +use crate::parser::registry::Signature; use crate::parser::{hir, Pipeline, PipelineElement, TokenNode}; use crate::prelude::*; @@ -61,8 +60,7 @@ fn load_plugin(path: &std::path::Path, context: &mut Context) -> Result<(), Shel let mut input = String::new(); match reader.read_line(&mut input) { Ok(_) => { - let response = - serde_json::from_str::>>(&input); + let response = serde_json::from_str::>>(&input); match response { Ok(jrpc) => match jrpc.params { Ok(params) => { @@ -70,15 +68,10 @@ fn load_plugin(path: &std::path::Path, context: &mut Context) -> Result<(), Shel if params.is_filter { let fname = fname.to_string(); let name = params.name.clone(); - context.add_commands(vec![Arc::new(PluginCommand::new( + context.add_commands(vec![static_command(PluginCommand::new( name, fname, params, ))]); Ok(()) - } else if params.is_sink { - let fname = fname.to_string(); - let name = params.name.clone(); - context.add_sinks(vec![Arc::new(PluginSink::new(name, fname, params))]); - Ok(()) } else { Ok(()) } @@ -158,10 +151,8 @@ pub async fn cli() -> Result<(), Box> { command("ps", Box::new(ps::ps)), command("ls", Box::new(ls::ls)), command("sysinfo", Box::new(sysinfo::sysinfo)), - command("cd", Box::new(cd::cd)), command("size", Box::new(size::size)), command("from-yaml", Box::new(from_yaml::from_yaml)), - command("get", Box::new(get::get)), command("exit", Box::new(exit::exit)), command("lines", Box::new(lines::lines)), command("split-column", Box::new(split_column::split_column)), @@ -175,19 +166,18 @@ pub async fn cli() -> Result<(), Box> { command("to-toml", Box::new(to_toml::to_toml)), command("to-yaml", Box::new(to_yaml::to_yaml)), command("sort-by", Box::new(sort_by::sort_by)), - Arc::new(Remove), - Arc::new(Open), - Arc::new(Where), - Arc::new(Config), - Arc::new(SkipWhile), - ]); - - context.add_sinks(vec![ - sink("autoview", Box::new(autoview::autoview)), - sink("clip", Box::new(clip::clip)), - sink("save", Box::new(save::save)), - sink("table", Box::new(table::table)), - sink("vtable", Box::new(vtable::vtable)), + static_command(Get), + static_command(Cd), + static_command(Remove), + static_command(Open), + static_command(Where), + static_command(Config), + static_command(SkipWhile), + static_command(Clip), + static_command(Autoview), + // command("save", Box::new(save::save)), + // command("table", Box::new(table::table)), + // command("vtable", Box::new(vtable::vtable)), ]); } let _ = load_plugins(&mut context); @@ -341,17 +331,19 @@ async fn process_line(readline: Result, ctx: &mut Context .map_err(|err| (line.clone(), err))?; match pipeline.commands.last() { - Some(ClassifiedCommand::Sink(_)) => {} Some(ClassifiedCommand::External(_)) => {} - _ => pipeline.commands.push(ClassifiedCommand::Sink(SinkCommand { - command: sink("autoview", Box::new(autoview::autoview)), - name_span: None, - args: hir::Call::new( - Box::new(hir::Expression::synthetic_string("autoview")), - None, - None, - ), - })), + _ => pipeline + .commands + .push(ClassifiedCommand::Internal(InternalCommand { + command: static_command(autoview::Autoview), + name_span: None, + source_map: ctx.source_map, + args: hir::Call::new( + Box::new(hir::Expression::synthetic_string("autoview")), + None, + None, + ), + })), } let mut input = ClassifiedInputStream::new(); @@ -379,18 +371,6 @@ async fn process_line(readline: Result, ctx: &mut Context ) } - (Some(ClassifiedCommand::Sink(SinkCommand { name_span, .. })), Some(_)) => { - return LineResult::Error(line.clone(), ShellError::maybe_labeled_error("Commands like table, save, and autoview must come last in the pipeline", "must come last", name_span)); - } - - (Some(ClassifiedCommand::Sink(left)), None) => { - let input_vec: Vec> = input.objects.into_vec().await; - if let Err(err) = left.run(ctx, input_vec, &Text::from(line)) { - return LineResult::Error(line.clone(), err); - } - break; - } - ( Some(ClassifiedCommand::Internal(left)), Some(ClassifiedCommand::External(_)), @@ -485,7 +465,7 @@ fn classify_command( match context.has_command(name) { true => { let command = context.get_command(name); - let config = command.config(); + let config = command.signature(); trace!(target: "nu::build_pipeline", "classifying {:?}", config); @@ -498,20 +478,8 @@ fn classify_command( args, })) } - false => match context.has_sink(name) { - true => { - let command = context.get_sink(name); - let config = command.config(); - - let args = config.parse_args(call, context.registry(), source)?; - - Ok(ClassifiedCommand::Sink(SinkCommand { - command, - name_span: Some(head.span().clone()), - args, - })) - } - false => { + false => match context.get_command(name).as_ref() { + Command::Static(command) => { let arg_list_strings: Vec> = match call.children() { //Some(args) => args.iter().map(|i| i.as_external_arg(source)).collect(), Some(args) => args diff --git a/src/commands.rs b/src/commands.rs index 9c60aabf16..1d0c5663b0 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -42,8 +42,15 @@ crate mod trim; crate mod vtable; crate mod where_; -crate use command::{command, EvaluatedStaticCommandArgs}; +crate use autoview::Autoview; +crate use cd::Cd; +crate use clip::Clip; +crate use command::{ + command, static_command, CallInfo, Command, CommandArgs, EvaluatedStaticCommandArgs, + StaticCommand, UnevaluatedCallInfo, +}; crate use config::Config; +crate use get::Get; crate use open::Open; crate use rm::Remove; crate use skip_while::SkipWhile; diff --git a/src/commands/autoview.rs b/src/commands/autoview.rs index 9a708615b0..d580294bc3 100644 --- a/src/commands/autoview.rs +++ b/src/commands/autoview.rs @@ -1,11 +1,31 @@ -use crate::commands::command::SinkCommandArgs; +use crate::commands::StaticCommand; use crate::context::{SourceMap, SpanSource}; use crate::errors::ShellError; use crate::format::GenericView; use crate::prelude::*; use std::path::Path; -pub fn autoview(args: SinkCommandArgs) -> Result<(), ShellError> { +pub struct Autoview; + +impl StaticCommand for Autoview { + fn name(&self) -> &str { + "autoview" + } + + fn run( + &self, + args: CommandArgs, + registry: &CommandRegistry, + ) -> Result { + args.process(registry, autoview)?.run() + } + + fn signature(&self) -> Signature { + Signature::build("autoview").sink() + } +} + +pub fn autoview(args: (), context: RunnableContext) -> Result { if args.input.len() > 0 { if let Spanned { item: Value::Binary(_), @@ -27,7 +47,7 @@ pub fn autoview(args: SinkCommandArgs) -> Result<(), ShellError> { } } - Ok(()) + Ok(OutputStream::empty()) } fn equal_shapes(input: &Vec>) -> bool { diff --git a/src/commands/cd.rs b/src/commands/cd.rs index 06fd327fa5..41b462d1d2 100644 --- a/src/commands/cd.rs +++ b/src/commands/cd.rs @@ -1,27 +1,54 @@ +use crate::commands::StaticCommand; use crate::errors::ShellError; use crate::prelude::*; use std::env; +use std::path::PathBuf; -pub fn cd(args: CommandArgs, registry: &CommandRegistry) -> Result { - let env = args.env.clone(); - let env = env.lock().unwrap(); - let args = args.evaluate_once(registry)?; - let cwd = env.path().to_path_buf(); +pub struct Cd; - let path = match args.nth(0) { +#[derive(Deserialize)] +pub struct CdArgs { + target: Option>, +} + +impl StaticCommand for Cd { + fn name(&self) -> &str { + "cd" + } + + fn signature(&self) -> Signature { + Signature::build("cd") + .optional("target", SyntaxType::Path) + .filter() + } + + fn run( + &self, + args: CommandArgs, + registry: &CommandRegistry, + ) -> Result { + args.process(registry, cd)?.run() + // cd(args, registry) + } +} + +pub fn cd(CdArgs { target }: CdArgs, context: RunnableContext) -> Result { + let cwd = context.cwd().to_path_buf(); + + let path = match &target { None => match dirs::home_dir() { Some(o) => o, _ => { return Err(ShellError::maybe_labeled_error( "Can not change to home directory", "can not go to home", - args.name_span(), + context.name, )) } }, Some(v) => { - let target = v.as_string()?; - match dunce::canonicalize(cwd.join(target).as_path()) { + // let target = v.item.as_string()?; + match dunce::canonicalize(cwd.join(&v.item()).as_path()) { Ok(p) => p, Err(_) => { return Err(ShellError::labeled_error( @@ -38,11 +65,11 @@ pub fn cd(args: CommandArgs, registry: &CommandRegistry) -> Result {} Err(_) => { - if args.len() > 0 { + if let Some(path) = target { return Err(ShellError::labeled_error( "Can not change to directory", "directory not found", - args.nth(0).unwrap().span.clone(), + path.span, )); } else { return Err(ShellError::string("Can not change to directory")); diff --git a/src/commands/classified.rs b/src/commands/classified.rs index 9abb7570eb..254415b525 100644 --- a/src/commands/classified.rs +++ b/src/commands/classified.rs @@ -1,6 +1,5 @@ -use crate::commands::command::Sink; +use crate::commands::Command; use crate::context::SourceMap; -use crate::evaluate::Scope; use crate::parser::{hir, Span, Spanned, TokenNode}; use crate::prelude::*; use bytes::{BufMut, BytesMut}; @@ -83,7 +82,6 @@ crate enum ClassifiedCommand { #[allow(unused)] Expr(TokenNode), Internal(InternalCommand), - Sink(SinkCommand), External(ExternalCommand), } @@ -93,34 +91,13 @@ impl ClassifiedCommand { match self { ClassifiedCommand::Expr(token) => token.span(), ClassifiedCommand::Internal(internal) => internal.name_span.into(), - ClassifiedCommand::Sink(sink) => sink.name_span.into(), ClassifiedCommand::External(external) => external.name_span.into(), } } } -crate struct SinkCommand { - crate command: Arc, - crate name_span: Option, - crate args: hir::Call, -} - -impl SinkCommand { - crate fn run( - self, - context: &mut Context, - input: Vec>, - source: &Text, - ) -> Result<(), ShellError> { - let args = self - .args - .evaluate(context.registry(), &Scope::empty(), source)?; - context.run_sink(self.command, self.name_span.clone(), args, input) - } -} - crate struct InternalCommand { - crate command: Arc, + crate command: Arc, crate name_span: Option, crate source_map: SourceMap, crate args: hir::Call, @@ -142,14 +119,16 @@ impl InternalCommand { let objects: InputStream = trace_stream!(target: "nu::trace_stream::internal", "input" = input.objects); - let result = context.run_command( - self.command, - self.name_span.clone(), - self.source_map, - self.args, - source, - objects, - )?; + let result = context + .run_command( + self.command, + self.name_span.clone(), + self.source_map, + self.args, + source, + objects, + ) + .await?; let mut result = result.values; diff --git a/src/commands/clip.rs b/src/commands/clip.rs index c2527234d9..0c07e80813 100644 --- a/src/commands/clip.rs +++ b/src/commands/clip.rs @@ -1,25 +1,71 @@ -use crate::commands::command::SinkCommandArgs; +use crate::commands::{CommandArgs, StaticCommand}; +use crate::context::CommandRegistry; use crate::errors::{labelled, ShellError}; +use crate::prelude::*; use clipboard::{ClipboardContext, ClipboardProvider}; +use futures::stream::StreamExt; +use futures_async_stream::async_stream_block; -pub fn clip(args: SinkCommandArgs) -> Result<(), ShellError> { +pub struct Clip; + +#[derive(Deserialize)] +pub struct ClipArgs {} + +impl StaticCommand for Clip { + fn name(&self) -> &str { + "clip" + } + fn run( + &self, + args: CommandArgs, + registry: &CommandRegistry, + ) -> Result { + args.process(registry, clip)?.run() + } + + fn signature(&self) -> Signature { + Signature::build("clip").sink() + } +} + +pub fn clip( + ClipArgs {}: ClipArgs, + RunnableContext { input, name, .. }: RunnableContext, +) -> Result { + let stream = async_stream_block! { + let values: Vec> = input.values.collect().await; + + inner_clip(values, name); + }; + + let stream: BoxStream<'static, ReturnValue> = stream.boxed(); + + Ok(OutputStream::from(stream)) +} + +async fn inner_clip(input: Vec>, name: Option) -> OutputStream { let mut clip_context: ClipboardContext = ClipboardProvider::new().unwrap(); let mut new_copy_data = String::new(); - if args.input.len() > 0 { + if input.len() > 0 { let mut first = true; - for i in args.input.iter() { + for i in input.iter() { if !first { new_copy_data.push_str("\n"); } else { first = false; } - let string = i.as_string().map_err(labelled( - args.name_span(), + let s = i.as_string().map_err(labelled( + name, "Given non-string data", "expected strings from pipeline", - ))?; + )); + + let string: String = match s { + Ok(string) => string, + Err(err) => return OutputStream::one(Err(err)), + }; new_copy_data.push_str(&string); } @@ -27,5 +73,5 @@ pub fn clip(args: SinkCommandArgs) -> Result<(), ShellError> { clip_context.set_contents(new_copy_data).unwrap(); - Ok(()) + OutputStream::empty() } diff --git a/src/commands/command.rs b/src/commands/command.rs index d3cd6ba86b..b1622b7b44 100644 --- a/src/commands/command.rs +++ b/src/commands/command.rs @@ -3,8 +3,9 @@ use crate::errors::ShellError; use crate::evaluate::Scope; use crate::object::Value; use crate::parser::hir; -use crate::parser::{registry, Span, Spanned}; +use crate::parser::{registry, ConfigDeserializer, Span, Spanned}; use crate::prelude::*; +use derive_new::new; use getset::Getters; use serde::{Deserialize, Serialize}; use std::fmt; @@ -80,6 +81,67 @@ impl CommandArgs { pub fn name_span(&self) -> Option { self.call_info.name_span } + + pub fn process<'de, T: Deserialize<'de>>( + self, + registry: &CommandRegistry, + callback: fn(T, RunnableContext) -> Result, + ) -> Result, ShellError> { + let env = self.env.clone(); + let args = self.evaluate_once(registry)?; + let (input, args) = args.split(); + let name_span = args.call_info.name_span; + let mut deserializer = ConfigDeserializer::from_call_node(args); + + Ok(RunnableArgs { + args: T::deserialize(&mut deserializer)?, + context: RunnableContext { + input: input, + env, + name: name_span, + }, + callback, + }) + } +} + +pub struct SinkContext { + pub input: Vec>, + pub env: Arc>, + pub name: Option, +} + +pub struct SinkArgs { + args: T, + context: SinkContext, + callback: fn(T, SinkContext) -> Result<(), ShellError>, +} + +pub struct RunnableContext { + pub input: InputStream, + pub env: Arc>, + pub name: Option, +} + +impl RunnableContext { + pub fn cwd(&self) -> PathBuf { + let env = self.env.clone(); + let env = env.lock().unwrap(); + + env.path.clone() + } +} + +pub struct RunnableArgs { + args: T, + context: RunnableContext, + callback: fn(T, RunnableContext) -> Result, +} + +impl RunnableArgs { + pub fn run(self) -> Result { + (self.callback)(self.args, self.context) + } } pub struct EvaluatedStaticCommandArgs { @@ -120,6 +182,12 @@ impl EvaluatedStaticCommandArgs { (input, args.call_info.args) } + + pub fn split(self) -> (InputStream, EvaluatedCommandArgs) { + let EvaluatedStaticCommandArgs { args, input } = self; + + (input, args) + } } #[derive(Getters)] @@ -155,7 +223,7 @@ impl EvaluatedFilterCommandArgs { } } -#[derive(Getters)] +#[derive(Getters, new)] #[get = "crate"] pub struct EvaluatedCommandArgs { pub host: Arc>, @@ -184,24 +252,21 @@ impl EvaluatedCommandArgs { self.call_info.args.get(name) } + pub fn slice_from(&self, from: usize) -> Vec> { + let positional = &self.call_info.args.positional; + + match positional { + None => vec![], + Some(list) => list[from..].to_vec(), + } + } + #[allow(unused)] pub fn has(&self, name: &str) -> bool { self.call_info.args.has(name) } } -pub struct SinkCommandArgs { - pub ctx: Context, - pub call_info: CallInfo, - pub input: Vec>, -} - -impl SinkCommandArgs { - pub fn name_span(&self) -> Option { - self.call_info.name_span - } -} - #[derive(Debug, Serialize, Deserialize)] pub enum CommandAction { ChangePath(PathBuf), @@ -241,38 +306,56 @@ impl ReturnSuccess { } } -pub trait Command: Send + Sync { +pub trait StaticCommand: Send + Sync { + fn name(&self) -> &str; + fn run( &self, args: CommandArgs, registry: ®istry::CommandRegistry, ) -> Result; - fn name(&self) -> &str; - fn config(&self) -> registry::CommandConfig { - registry::CommandConfig { + fn signature(&self) -> Signature { + Signature { name: self.name().to_string(), positional: vec![], rest_positional: true, named: indexmap::IndexMap::new(), is_filter: true, - is_sink: false, } } } -pub trait Sink { - fn run(&self, args: SinkCommandArgs) -> Result<(), ShellError>; - fn name(&self) -> &str; +pub enum Command { + Static(Arc), +} - fn config(&self) -> registry::CommandConfig { - registry::CommandConfig { - name: self.name().to_string(), - positional: vec![], - rest_positional: true, - named: indexmap::IndexMap::new(), - is_filter: false, - is_sink: true, +impl Command { + pub fn name(&self) -> &str { + match self { + Command::Static(command) => command.name(), + } + } + + pub fn is_sink(&self) -> bool { + match self { + Command::Static(..) => false, + } + } + + pub fn signature(&self) -> Signature { + match self { + Command::Static(command) => command.signature(), + } + } + + pub async fn run( + &self, + args: CommandArgs, + registry: ®istry::CommandRegistry, + ) -> Result { + match self { + Command::Static(command) => command.run(args, registry), } } } @@ -283,7 +366,7 @@ pub struct FnFilterCommand { func: fn(EvaluatedFilterCommandArgs) -> Result, } -impl Command for FnFilterCommand { +impl StaticCommand for FnFilterCommand { fn name(&self) -> &str { &self.name } @@ -339,7 +422,11 @@ pub struct FnRawCommand { >, } -impl Command for FnRawCommand { +impl StaticCommand for FnRawCommand { + fn name(&self) -> &str { + &self.name + } + fn run( &self, args: CommandArgs, @@ -347,10 +434,6 @@ impl Command for FnRawCommand { ) -> Result { (self.func)(args, registry) } - - fn name(&self) -> &str { - &self.name - } } pub fn command( @@ -360,45 +443,24 @@ pub fn command( + Send + Sync, >, -) -> Arc { - Arc::new(FnRawCommand { +) -> Arc { + Arc::new(Command::Static(Arc::new(FnRawCommand { name: name.to_string(), func, - }) + }))) +} + +pub fn static_command(command: impl StaticCommand + 'static) -> Arc { + Arc::new(Command::Static(Arc::new(command))) } #[allow(unused)] pub fn filter( name: &str, func: fn(EvaluatedFilterCommandArgs) -> Result, -) -> Arc { - Arc::new(FnFilterCommand { +) -> Arc { + Arc::new(Command::Static(Arc::new(FnFilterCommand { name: name.to_string(), func, - }) -} - -pub struct FnSink { - name: String, - func: Box Result<(), ShellError>>, -} - -impl Sink for FnSink { - fn run(&self, args: SinkCommandArgs) -> Result<(), ShellError> { - (self.func)(args) - } - - fn name(&self) -> &str { - &self.name - } -} - -pub fn sink( - name: &str, - func: Box Result<(), ShellError>>, -) -> Arc { - Arc::new(FnSink { - name: name.to_string(), - func, - }) + }))) } diff --git a/src/commands/config.rs b/src/commands/config.rs index 6daf062098..c0d8334023 100644 --- a/src/commands/config.rs +++ b/src/commands/config.rs @@ -1,57 +1,61 @@ use crate::prelude::*; -use crate::commands::EvaluatedStaticCommandArgs; +use crate::commands::StaticCommand; use crate::errors::ShellError; use crate::object::{config, Value}; use crate::parser::hir::SyntaxType; -use crate::parser::registry::{self, CommandConfig, NamedType}; -use indexmap::IndexMap; -use log::trace; +use crate::parser::registry::{self}; use std::iter::FromIterator; pub struct Config; -impl Command for Config { +#[derive(Deserialize)] +pub struct ConfigArgs { + set: Option<(Spanned, Spanned)>, + get: Option>, + clear: Spanned, + remove: Option>, + path: Spanned, +} + +impl StaticCommand for Config { + fn name(&self) -> &str { + "config" + } + + fn signature(&self) -> Signature { + Signature::build("config") + .named("set", SyntaxType::Any) + .named("get", SyntaxType::Any) + .named("remove", SyntaxType::Any) + .switch("clear") + .switch("path") + .sink() + } + fn run( &self, args: CommandArgs, registry: ®istry::CommandRegistry, ) -> Result { - let args = args.evaluate_once(registry)?; - config(args) - } - - fn name(&self) -> &str { - "config" - } - - fn config(&self) -> CommandConfig { - let mut named: IndexMap = IndexMap::new(); - named.insert("set".to_string(), NamedType::Optional(SyntaxType::Any)); - named.insert("get".to_string(), NamedType::Optional(SyntaxType::Any)); - named.insert("clear".to_string(), NamedType::Switch); - - named.insert("remove".to_string(), NamedType::Optional(SyntaxType::Any)); - - CommandConfig { - name: self.name().to_string(), - positional: vec![], - rest_positional: false, - named, - is_sink: true, - is_filter: false, - } + args.process(registry, config)?.run() } } -pub fn config(args: EvaluatedStaticCommandArgs) -> Result { - let mut result = crate::object::config::config(args.name_span())?; +pub fn config( + ConfigArgs { + set, + get, + clear, + remove, + path, + }: ConfigArgs, + RunnableContext { name, .. }: RunnableContext, +) -> Result { + let mut result = crate::object::config::config(name)?; - trace!("{:#?}", args.call_args().positional); - trace!("{:#?}", args.call_args().named); - - if let Some(v) = args.get("get") { - let key = v.as_string()?; + if let Some(v) = get { + let key = v.to_string(); let value = result .get(&key) .ok_or_else(|| ShellError::string(&format!("Missing key {} in config", key)))?; @@ -61,31 +65,38 @@ pub fn config(args: EvaluatedStaticCommandArgs) -> Result Result) -> Spanned { +pub fn convert_toml_value_to_nu_value(v: &toml::Value, span: impl Into) -> Spanned { let span = span.into(); match v { diff --git a/src/commands/get.rs b/src/commands/get.rs index ed5d656354..b90ddd837f 100644 --- a/src/commands/get.rs +++ b/src/commands/get.rs @@ -1,9 +1,33 @@ +use crate::commands::StaticCommand; use crate::errors::ShellError; use crate::object::Value; -use crate::parser::Span; use crate::prelude::*; -fn get_member(path: &str, span: Span, obj: &Spanned) -> Result, ShellError> { +pub struct Get; + +#[derive(Deserialize)] +pub struct GetArgs { + rest: Vec>, +} + +impl StaticCommand for Get { + fn name(&self) -> &str { + "get" + } + + fn run( + &self, + args: CommandArgs, + registry: &CommandRegistry, + ) -> Result { + args.process(registry, get)?.run() + } + fn signature(&self) -> Signature { + Signature::build("get").rest() + } +} + +fn get_member(path: &Spanned, obj: &Spanned) -> Result, ShellError> { let mut current = obj; for p in path.split(".") { match current.get_data_by_key(p) { @@ -12,7 +36,7 @@ fn get_member(path: &str, span: Span, obj: &Spanned) -> Result) -> Result Result { - let args = args.evaluate_once(registry)?; - let span = args.name_span(); - let len = args.len(); - - if len == 0 { - return Err(ShellError::maybe_labeled_error( - "Get requires a field or field path", - "needs parameter", - span, - )); - } - - let amount = args.expect_nth(0)?.as_i64(); - let (input, args) = args.parts(); - let positional = args.positional; - +pub fn get( + GetArgs { rest: fields }: GetArgs, + RunnableContext { input, .. }: RunnableContext, +) -> Result { // If it's a number, get the row instead of the column - if let Ok(amount) = amount { - return Ok(input.values.skip(amount as u64).take(1).from_input_stream()); - } - - let fields: Result, _> = positional - .iter() - .flatten() - .map(|a| (a.as_string().map(|x| (x, a.span)))) - .collect(); - - let fields = fields?; + // if let Some(amount) = amount { + // return Ok(input.values.skip(amount as u64).take(1).from_input_stream()); + // } let stream = input .values .map(move |item| { let mut result = VecDeque::new(); for field in &fields { - match get_member(&field.0, field.1, &item) { + match get_member(field, &item) { Ok(Spanned { item: Value::List(l), .. diff --git a/src/commands/macros.rs b/src/commands/macros.rs index 0fe491e32d..8e148de5ff 100644 --- a/src/commands/macros.rs +++ b/src/commands/macros.rs @@ -12,7 +12,7 @@ macro_rules! command { Named { $export:tt $args:ident $body:block } Positional { $($number:tt)* } Rest {} - CommandConfig { + Signature { name: $config_name:tt, mandatory_positional: vec![ $($mandatory_positional:tt)* ], optional_positional: vec![ $($optional_positional:tt)* ], @@ -52,8 +52,8 @@ macro_rules! command { stringify!($config_name) } - fn config(&self) -> $crate::parser::registry::CommandConfig { - $crate::parser::registry::CommandConfig { + fn config(&self) -> $crate::parser::registry::Signature { + $crate::parser::registry::Signature { name: self.name().to_string(), positional: vec![$($mandatory_positional)*], rest_positional: false, @@ -82,7 +82,7 @@ macro_rules! command { Named { $export:tt $args:ident $body:block } Positional { $($positional_count:tt)* } Rest { -- $param_name:ident : Switch , $($rest:tt)* } - CommandConfig { + Signature { name: $config_name:tt, mandatory_positional: vec![ $($mandatory_positional:tt)* ], optional_positional: vec![ $($optional_positional:tt)* ], @@ -102,7 +102,7 @@ macro_rules! command { Named { $export $args $body } Positional { $($positional_count)* + 1 } Rest { $($rest)* } - CommandConfig { + Signature { name: $config_name, mandatory_positional: vec![ $($mandatory_positional)* ], optional_positional: vec![ $($optional_positional)* ], @@ -132,7 +132,7 @@ macro_rules! command { Named { $export:tt $args:ident $body:block } Positional { $($positional_count:tt)* } Rest { -- $param_name:ident : $param_kind:ty , $($rest:tt)* } - CommandConfig { + Signature { name: $config_name:tt, mandatory_positional: vec![ $($mandatory_positional:tt)* ], optional_positional: vec![ $($optional_positional:tt)* ], @@ -152,7 +152,7 @@ macro_rules! command { Named { $export $args $body } Positional { $($positional_count)* + 1 } Rest { $($rest)* } - CommandConfig { + Signature { name: $config_name, mandatory_positional: vec![ $($mandatory_positional)* ], optional_positional: vec![ $($optional_positional)* ], @@ -182,7 +182,7 @@ macro_rules! command { Named { $export:tt $args:ident $body:block } Positional { $($positional_count:tt)* } Rest { -- $param_name:ident ? : $param_kind:ty , $($rest:tt)* } - CommandConfig { + Signature { name: $config_name:tt, mandatory_positional: vec![ $($mandatory_positional:tt)* ], optional_positional: vec![ $($optional_positional:tt)* ], @@ -202,7 +202,7 @@ macro_rules! command { Named { $export $args $body } Positional { $($positional_count)* + 1 } Rest { $($rest)* } - CommandConfig { + Signature { name: $config_name, mandatory_positional: vec![ $($mandatory_positional)* ], optional_positional: vec![ $($optional_positional)* ], @@ -232,7 +232,7 @@ macro_rules! command { Named { $export:ident $args:ident $body:block } Positional { $($positional_count:tt)* } Rest { $param_name:ident : Block , $($rest:tt)* } - CommandConfig { + Signature { name: $config_name:tt, mandatory_positional: vec![ $($mandatory_positional:tt)* ], optional_positional: vec![ $($optional_positional:tt)* ], @@ -255,7 +255,7 @@ macro_rules! command { Named { $export $args $body } Positional { $($positional_count)* + 1 } Rest { $($rest)* } - CommandConfig { + Signature { name: $config_name, mandatory_positional: vec![ $($mandatory_positional)* $crate::parser::registry::PositionalType::mandatory_block( stringify!($param_name) @@ -287,7 +287,7 @@ macro_rules! command { Named { $export:ident $args:ident $body:block } Positional { $($positional_count:tt)* } Rest { $param_name:ident : $param_kind:ty , $($rest:tt)* } - CommandConfig { + Signature { name: $config_name:tt, mandatory_positional: vec![ $($mandatory_positional:tt)* ], optional_positional: vec![ $($optional_positional:tt)* ], @@ -310,7 +310,7 @@ macro_rules! command { Named { $export $args $body } Positional { $($positional_count)* + 1 } Rest { $($rest)* } - CommandConfig { + Signature { name: $config_name, mandatory_positional: vec![ $($mandatory_positional)* $crate::parser::registry::PositionalType::mandatory( stringify!($param_name), <$param_kind>::syntax_type() @@ -341,7 +341,7 @@ macro_rules! command { Named { $export $args $body } Positional { 0 } Rest { $($command_rest)* } - CommandConfig { + Signature { name: $config_name, mandatory_positional: vec![], optional_positional: vec![], @@ -377,11 +377,11 @@ macro_rules! command { // stringify!($name) // } - // fn config(&self) -> CommandConfig { + // fn config(&self) -> Signature { // let mut named: IndexMap = IndexMap::new(); // named.insert(stringify!($param).to_string(), NamedType::$kind); - // CommandConfig { + // Signature { // name: self.name().to_string(), // mandatory_positional: vec![], // optional_positional: vec![], diff --git a/src/commands/open.rs b/src/commands/open.rs index 172526e3e2..3739e46b12 100644 --- a/src/commands/open.rs +++ b/src/commands/open.rs @@ -1,9 +1,10 @@ +use crate::commands::StaticCommand; use crate::context::SpanSource; use crate::errors::ShellError; use crate::object::{Primitive, Value}; use crate::parser::hir::SyntaxType; use crate::parser::parse::span::Span; -use crate::parser::registry::{self, CommandConfig, NamedType}; +use crate::parser::registry::{self, Signature}; use crate::prelude::*; use mime::Mime; use std::path::{Path, PathBuf}; @@ -12,75 +13,81 @@ use uuid::Uuid; pub struct Open; -impl Command for Open { +#[derive(Deserialize)] +pub struct OpenArgs { + path: Spanned, + raw: bool, +} + +impl StaticCommand for Open { + fn name(&self) -> &str { + "open" + } + + fn signature(&self) -> Signature { + Signature::build(self.name()) + .required("path", SyntaxType::Block) + .switch("raw") + .sink() + } + fn run( &self, args: CommandArgs, registry: ®istry::CommandRegistry, ) -> Result { - let env = args.env.clone(); - let args = args.evaluate_once(registry)?; - let path = >::extract(args.expect_nth(0)?)?; - let raw = args.has("raw"); + args.process(registry, run)?.run() + } +} - let span = args.name_span(); +fn run( + OpenArgs { raw, path }: OpenArgs, + RunnableContext { env, name, .. }: RunnableContext, +) -> Result { + let cwd = env.lock().unwrap().path().to_path_buf(); + let full_path = PathBuf::from(cwd); - let cwd = env.lock().unwrap().path().to_path_buf(); - let full_path = PathBuf::from(cwd); + let path_str = path.to_str().ok_or(ShellError::type_error( + "Path", + "invalid path".spanned(path.span), + ))?; - let path_str = path.to_str().ok_or(ShellError::type_error( - "Path", - "invalid path".spanned(path.span), - ))?; + let (file_extension, contents, contents_span, span_source) = + fetch(&full_path, path_str, path.span)?; - let (file_extension, contents, contents_span, span_source) = - fetch(&full_path, path_str, path.span)?; + let file_extension = if raw { None } else { file_extension }; - let file_extension = if raw { None } else { file_extension }; + let mut stream = VecDeque::new(); - let mut stream = VecDeque::new(); + if let Some(uuid) = contents_span.source { + // If we have loaded something, track its source + stream.push_back(ReturnSuccess::action(CommandAction::AddSpanSource( + uuid, + span_source, + ))) + } - if let Some(uuid) = contents_span.source { - // If we have loaded something, track its source - stream.push_back(ReturnSuccess::action(CommandAction::AddSpanSource( - uuid, - span_source, - ))) + match contents { + Value::Primitive(Primitive::String(string)) => { + let value = parse_as_value(file_extension, string, contents_span, name)?; + + match value { + Spanned { + item: Value::List(list), + .. + } => { + for elem in list { + stream.push_back(ReturnSuccess::value(elem)); + } + } + x => stream.push_back(ReturnSuccess::value(x)), + } } - match contents { - Value::Primitive(Primitive::String(string)) => { - let value = parse_as_value(file_extension, string, contents_span, span)?; + other => stream.push_back(ReturnSuccess::value(other.spanned(contents_span))), + }; - match value { - Spanned { - item: Value::List(list), - .. - } => { - for elem in list { - stream.push_back(ReturnSuccess::value(elem)); - } - } - x => stream.push_back(ReturnSuccess::value(x)), - } - } - - other => stream.push_back(ReturnSuccess::value(other.spanned(contents_span))), - }; - - Ok(stream.boxed().to_output_stream()) - } - - fn name(&self) -> &str { - "open" - } - - fn config(&self) -> CommandConfig { - CommandConfig::new(self.name()) - .required("path", SyntaxType::Block) - .named("raw", NamedType::Switch) - .sink() - } + Ok(stream.boxed().to_output_stream()) } // command! { diff --git a/src/commands/plugin.rs b/src/commands/plugin.rs index 0b9b060f77..902f4bbef1 100644 --- a/src/commands/plugin.rs +++ b/src/commands/plugin.rs @@ -1,7 +1,9 @@ +use crate::commands::StaticCommand; use crate::errors::ShellError; use crate::parser::registry; use crate::prelude::*; use derive_new::new; +use futures_async_stream::async_stream_block; use serde::{self, Deserialize, Serialize}; use std::io::prelude::*; use std::io::BufReader; @@ -37,10 +39,18 @@ pub enum NuResult { pub struct PluginCommand { name: String, path: String, - config: registry::CommandConfig, + config: registry::Signature, } -impl Command for PluginCommand { +impl StaticCommand for PluginCommand { + fn name(&self) -> &str { + &self.name + } + + fn signature(&self) -> registry::Signature { + self.config.clone() + } + fn run( &self, args: CommandArgs, @@ -48,29 +58,36 @@ impl Command for PluginCommand { ) -> Result { filter_plugin(self.path.clone(), args, registry) } - fn name(&self) -> &str { - &self.name - } - fn config(&self) -> registry::CommandConfig { - self.config.clone() - } } #[derive(new)] pub struct PluginSink { - name: String, path: String, - config: registry::CommandConfig, + config: registry::Signature, } -impl Sink for PluginSink { - fn run(&self, args: SinkCommandArgs) -> Result<(), ShellError> { - sink_plugin(self.path.clone(), args) +impl StaticCommand for PluginSink { + fn run( + &self, + args: CommandArgs, + registry: &CommandRegistry, + ) -> Result { + let path = self.path.clone(); + + let stream = async_stream_block! { + sink_plugin(path, args).await; + }; + + let stream: BoxStream<'static, ReturnValue> = stream.boxed(); + + Ok(OutputStream::from(stream)) } + fn name(&self) -> &str { - &self.name + &self.config.name } - fn config(&self) -> registry::CommandConfig { + + fn signature(&self) -> registry::Signature { self.config.clone() } } @@ -191,9 +208,10 @@ pub fn filter_plugin( Ok(stream.to_output_stream()) } -pub fn sink_plugin(path: String, args: SinkCommandArgs) -> Result<(), ShellError> { +pub async fn sink_plugin(path: String, args: CommandArgs) -> Result { //use subprocess::Exec; - let request = JsonRpc::new("sink", (args.call_info, args.input)); + let input: Vec> = args.input.values.collect().await; + let request = JsonRpc::new("sink", (args.call_info, input)); let request_raw = serde_json::to_string(&request).unwrap(); let mut tmpfile = tempfile::NamedTempFile::new()?; let _ = writeln!(tmpfile, "{}", request_raw); @@ -206,5 +224,5 @@ pub fn sink_plugin(path: String, args: SinkCommandArgs) -> Result<(), ShellError let _ = child.wait(); - Ok(()) + Ok(OutputStream::empty()) } diff --git a/src/commands/rm.rs b/src/commands/rm.rs index 5a4ca7cacd..44403a078e 100644 --- a/src/commands/rm.rs +++ b/src/commands/rm.rs @@ -1,63 +1,57 @@ -use crate::commands::EvaluatedStaticCommandArgs; +use crate::commands::{EvaluatedStaticCommandArgs, StaticCommand}; use crate::errors::ShellError; use crate::parser::hir::SyntaxType; -use crate::parser::registry::{CommandConfig, NamedType, PositionalType}; +use crate::parser::registry::{NamedType, PositionalType}; use crate::prelude::*; use indexmap::IndexMap; +use std::path::PathBuf; pub struct Remove; -impl Command for Remove { +#[derive(Deserialize)] +pub struct RemoveArgs { + path: Spanned, + recursive: bool, +} + +impl StaticCommand for Remove { + fn name(&self) -> &str { + "rm" + } + + fn signature(&self) -> Signature { + Signature::build("rm") + .required("path", SyntaxType::Path) + .switch("recursive") + .sink() + } + fn run( &self, args: CommandArgs, registry: &CommandRegistry, ) -> Result { - let env = args.env.clone(); - rm(args.evaluate_once(registry)?, env) - } - - fn name(&self) -> &str { - "rm" - } - - fn config(&self) -> CommandConfig { - let mut named: IndexMap = IndexMap::new(); - named.insert("recursive".to_string(), NamedType::Switch); - - CommandConfig { - name: self.name().to_string(), - positional: vec![PositionalType::mandatory("file", SyntaxType::Path)], - rest_positional: false, - named, - is_sink: true, - is_filter: false, - } + args.process(registry, rm)?.run() } } pub fn rm( - args: EvaluatedStaticCommandArgs, - env: Arc>, + RemoveArgs { path, recursive }: RemoveArgs, + context: RunnableContext, ) -> Result { - let mut full_path = env.lock().unwrap().path().to_path_buf(); + let mut full_path = context.cwd(); - match args - .nth(0) - .ok_or_else(|| ShellError::string(&format!("No file or directory specified")))? - .as_string()? - .as_str() - { + match path.item.to_str().unwrap() { "." | ".." => return Err(ShellError::string("\".\" and \"..\" may not be removed")), file => full_path.push(file), } if full_path.is_dir() { - if !args.has("recursive") { - return Err(ShellError::labeled_error( + if !recursive { + return Err(ShellError::maybe_labeled_error( "is a directory", "", - args.name_span().unwrap(), + context.name, )); } std::fs::remove_dir_all(&full_path).expect("can not remove directory"); diff --git a/src/commands/save.rs b/src/commands/save.rs index 4b3152987b..47b838923e 100644 --- a/src/commands/save.rs +++ b/src/commands/save.rs @@ -1,100 +1,119 @@ -use crate::commands::command::SinkCommandArgs; use crate::commands::to_csv::{to_string as to_csv_to_string, value_to_csv_value}; use crate::commands::to_json::value_to_json_value; use crate::commands::to_toml::value_to_toml_value; use crate::commands::to_yaml::value_to_yaml_value; +use crate::commands::StaticCommand; use crate::errors::ShellError; use crate::object::{Primitive, Value}; use crate::parser::Spanned; +use crate::prelude::*; +use futures_async_stream::async_stream_block; use std::path::{Path, PathBuf}; -pub fn save(args: SinkCommandArgs) -> Result<(), ShellError> { - if args.call_info.args.positional.is_none() { - return Err(ShellError::maybe_labeled_error( - "Save requires a filepath", - "needs path", - args.name_span(), - )); - } +pub struct Save; - let positional = match args.call_info.args.positional { - None => return Err(ShellError::string("save requires a filepath")), - Some(p) => p, - }; - - let cwd = args.ctx.env.lock().unwrap().path().to_path_buf(); - let mut full_path = PathBuf::from(cwd); - match &(positional[0].item) { - Value::Primitive(Primitive::String(s)) => full_path.push(Path::new(s)), - _ => {} - } - - let save_raw = match positional.get(1) { - Some(Spanned { - item: Value::Primitive(Primitive::String(s)), - .. - }) if s == "--raw" => true, - _ => false, - }; - - let contents = match full_path.extension() { - Some(x) if x == "csv" && !save_raw => { - if args.input.len() != 1 { - return Err(ShellError::string( - "saving to csv requires a single object (or use --raw)", - )); - } - to_csv_to_string(&value_to_csv_value(&args.input[0])).unwrap() - } - Some(x) if x == "toml" && !save_raw => { - if args.input.len() != 1 { - return Err(ShellError::string( - "saving to toml requires a single object (or use --raw)", - )); - } - toml::to_string(&value_to_toml_value(&args.input[0])).unwrap() - } - Some(x) if x == "json" && !save_raw => { - if args.input.len() != 1 { - return Err(ShellError::string( - "saving to json requires a single object (or use --raw)", - )); - } - serde_json::to_string(&value_to_json_value(&args.input[0])).unwrap() - } - Some(x) if x == "yml" && !save_raw => { - if args.input.len() != 1 { - return Err(ShellError::string( - "saving to yml requires a single object (or use --raw)", - )); - } - serde_yaml::to_string(&value_to_yaml_value(&args.input[0])).unwrap() - } - Some(x) if x == "yaml" && !save_raw => { - if args.input.len() != 1 { - return Err(ShellError::string( - "saving to yaml requires a single object (or use --raw)", - )); - } - serde_yaml::to_string(&value_to_yaml_value(&args.input[0])).unwrap() - } - _ => { - let mut save_data = String::new(); - if args.input.len() > 0 { - let mut first = true; - for i in args.input.iter() { - if !first { - save_data.push_str("\n"); - } else { - first = false; - } - save_data.push_str(&i.as_string().unwrap()); - } - } - save_data - } - }; - - let _ = std::fs::write(full_path, contents); - Ok(()) +#[derive(Deserialize)] +struct SaveArgs { + path: Spanned, + raw: bool, +} + +impl StaticCommand for Save { + fn name(&self) -> &str { + "save" + } + + fn signature(&self) -> Signature { + Signature::build("save") + .required("path", SyntaxType::Path) + .switch("raw") + .sink() + } + + fn run( + &self, + args: CommandArgs, + registry: &CommandRegistry, + ) -> Result { + args.process(registry, save)?.run() + } +} + +pub fn save( + SaveArgs { + path, + raw: save_raw, + }: SaveArgs, + context: RunnableContext, +) -> Result { + let mut full_path = context.cwd(); + full_path.push(path.item()); + + let stream = async_stream_block! { + let input: Vec> = context.input.values.collect().await; + + let contents = match full_path.extension() { + Some(x) if x == "csv" && !save_raw => { + if input.len() != 1 { + return Err(ShellError::string( + "saving to csv requires a single object (or use --raw)", + )); + } + to_csv_to_string(&value_to_csv_value(&input[0])).unwrap() + } + Some(x) if x == "toml" && !save_raw => { + if input.len() != 1 { + return Err(ShellError::string( + "saving to toml requires a single object (or use --raw)", + )); + } + toml::to_string(&value_to_toml_value(&input[0])).unwrap() + } + Some(x) if x == "json" && !save_raw => { + if input.len() != 1 { + return Err(ShellError::string( + "saving to json requires a single object (or use --raw)", + )); + } + serde_json::to_string(&value_to_json_value(&input[0])).unwrap() + } + Some(x) if x == "yml" && !save_raw => { + if input.len() != 1 { + return Err(ShellError::string( + "saving to yml requires a single object (or use --raw)", + )); + } + serde_yaml::to_string(&value_to_yaml_value(&input[0])).unwrap() + } + Some(x) if x == "yaml" && !save_raw => { + if input.len() != 1 { + return Err(ShellError::string( + "saving to yaml requires a single object (or use --raw)", + )); + } + serde_yaml::to_string(&value_to_yaml_value(&input[0])).unwrap() + } + _ => { + let mut save_data = String::new(); + if input.len() > 0 { + let mut first = true; + for i in input.iter() { + if !first { + save_data.push_str("\n"); + } else { + first = false; + } + save_data.push_str(&i.as_string().unwrap()); + } + } + save_data + } + }; + + let _ = std::fs::write(full_path, contents); + }; + + let stream: BoxStream<'static, ReturnValue> = stream.boxed(); + + Ok(OutputStream::from(stream)) } diff --git a/src/commands/skip_while.rs b/src/commands/skip_while.rs index 47df16a480..c04befd553 100644 --- a/src/commands/skip_while.rs +++ b/src/commands/skip_while.rs @@ -1,54 +1,40 @@ +use crate::commands::StaticCommand; use crate::errors::ShellError; -use crate::parser::registry::CommandConfig; -use crate::parser::registry::PositionalType; use crate::prelude::*; pub struct SkipWhile; -impl Command for SkipWhile { +#[derive(Deserialize)] +pub struct SkipWhileArgs { + condition: value::Block, +} + +impl StaticCommand for SkipWhile { + fn name(&self) -> &str { + "skip-while" + } + + fn signature(&self) -> Signature { + Signature::build("skip-while") + .required("condition", SyntaxType::Block) + .filter() + } + fn run( &self, args: CommandArgs, registry: &CommandRegistry, ) -> Result { - skip_while(args, registry) - } - fn name(&self) -> &str { - "skip-while" - } - - fn config(&self) -> CommandConfig { - CommandConfig { - name: self.name().to_string(), - positional: vec![PositionalType::mandatory_block("condition")], - rest_positional: false, - named: indexmap::IndexMap::new(), - is_filter: true, - is_sink: false, - } + args.process(registry, skip_while)?.run() } } pub fn skip_while( - args: CommandArgs, - registry: &CommandRegistry, + SkipWhileArgs { condition }: SkipWhileArgs, + RunnableContext { input, .. }: RunnableContext, ) -> Result { - let args = args.evaluate_once(registry)?; - let block = args.expect_nth(0)?.as_block()?; - let span = args.name_span(); - let len = args.len(); - let input = args.input; - - if len == 0 { - return Err(ShellError::maybe_labeled_error( - "Where requires a condition", - "needs condition", - span, - )); - } - let objects = input.values.skip_while(move |item| { - let result = block.invoke(&item); + let result = condition.invoke(&item); let return_value = match result { Ok(v) if v.is_true() => true, diff --git a/src/commands/table.rs b/src/commands/table.rs index d8e3ca7f2a..02ce7cd384 100644 --- a/src/commands/table.rs +++ b/src/commands/table.rs @@ -1,9 +1,8 @@ -use crate::commands::command::SinkCommandArgs; use crate::errors::ShellError; use crate::format::TableView; use crate::prelude::*; -pub fn table(args: SinkCommandArgs) -> Result<(), ShellError> { +pub fn table(args: CommandArgs, context: RunnableContext) -> Result<(), ShellError> { if args.input.len() > 0 { let mut host = args.ctx.host.lock().unwrap(); let view = TableView::from_list(&args.input); diff --git a/src/commands/vtable.rs b/src/commands/vtable.rs index 827170a2f0..deb67b9d22 100644 --- a/src/commands/vtable.rs +++ b/src/commands/vtable.rs @@ -1,9 +1,8 @@ -use crate::commands::command::SinkCommandArgs; use crate::errors::ShellError; use crate::format::VTableView; use crate::prelude::*; -pub fn vtable(args: SinkCommandArgs) -> Result<(), ShellError> { +pub fn vtable(args: CommandArgs, context: RunnableContext) -> Result<(), ShellError> { if args.input.len() > 0 { let mut host = args.ctx.host.lock().unwrap(); let view = VTableView::from_list(&args.input); diff --git a/src/commands/where_.rs b/src/commands/where_.rs index d9480aa4ed..14b899c836 100644 --- a/src/commands/where_.rs +++ b/src/commands/where_.rs @@ -1,75 +1,58 @@ +use crate::commands::StaticCommand; use crate::errors::ShellError; use crate::object::base as value; use crate::parser::hir::SyntaxType; -use crate::parser::registry::{self, CommandConfig, PositionalType}; +use crate::parser::registry; use crate::prelude::*; use futures::future::ready; -use indexmap::IndexMap; -use log::trace; +use serde::Deserialize; pub struct Where; -impl Command for Where { +#[derive(Deserialize)] +struct WhereArgs { + condition: value::Block, +} + +impl StaticCommand for Where { + fn name(&self) -> &str { + "where" + } + + fn signature(&self) -> registry::Signature { + Signature::build("where") + .required("condition", SyntaxType::Block) + .sink() + } + fn run( &self, args: CommandArgs, registry: ®istry::CommandRegistry, ) -> Result { - let args = args.evaluate_once(registry)?; - let condition = value::Block::extract(args.expect_nth(0)?)?; - let input = args.input; - let input: InputStream = - trace_stream!(target: "nu::trace_stream::where", "where input" = input); - - Ok(input - .values - .filter_map(move |item| { - let result = condition.invoke(&item); - - let return_value = match result { - Err(err) => Some(Err(err)), - Ok(v) if v.is_true() => Some(Ok(ReturnSuccess::Value(item.clone()))), - _ => None, - }; - - ready(return_value) - }) - .boxed() - .to_output_stream()) - } - - fn name(&self) -> &str { - "where" - } - - fn config(&self) -> CommandConfig { - CommandConfig { - name: self.name().to_string(), - positional: vec![PositionalType::mandatory("condition", SyntaxType::Block)], - rest_positional: false, - named: IndexMap::default(), - is_sink: true, - is_filter: false, - } + args.process(registry, run)?.run() } } -// command! { -// Where as where(args, condition: Block,) { -// let input = args.input; -// let input: InputStream = trace_stream!(target: "nu::trace_stream::where", "where input" = input); +fn run( + WhereArgs { condition }: WhereArgs, + context: RunnableContext, +) -> Result { + Ok(context + .input + .values + .filter_map(move |item| { + let result = condition.invoke(&item); -// input.values.filter_map(move |item| { -// let result = condition.invoke(&item); + let return_value = match result { + Err(err) => Some(Err(err)), + Ok(v) if v.is_true() => Some(Ok(ReturnSuccess::Value(item.clone()))), + _ => None, + }; -// let return_value = match result { -// Err(err) => Some(Err(err)), -// Ok(v) if v.is_true() => Some(Ok(ReturnSuccess::Value(item.clone()))), -// _ => None, -// }; - -// ready(return_value) -// }) -// } -// } + ready(return_value) + }) + .boxed() + .to_output_stream()) +} diff --git a/src/context.rs b/src/context.rs index 1eed5d39b7..75053f0c5d 100644 --- a/src/context.rs +++ b/src/context.rs @@ -1,4 +1,4 @@ -use crate::commands::command::{CallInfo, Sink, SinkCommandArgs, UnevaluatedCallInfo}; +use crate::commands::{CallInfo, Command, StaticCommand, UnevaluatedCallInfo}; use crate::parser::{hir, registry, Span}; use crate::prelude::*; @@ -37,7 +37,7 @@ impl SourceMap { #[derive(Clone, new)] pub struct CommandRegistry { #[new(value = "Arc::new(Mutex::new(IndexMap::default()))")] - registry: Arc>>>, + registry: Arc>>>, } impl CommandRegistry { @@ -47,7 +47,7 @@ impl CommandRegistry { } } - fn get_command(&self, name: &str) -> Option> { + fn get_command(&self, name: &str) -> Option> { let registry = self.registry.lock().unwrap(); registry.get(name).map(|c| c.clone()) @@ -59,7 +59,7 @@ impl CommandRegistry { registry.contains_key(name) } - fn insert(&mut self, name: impl Into, command: Arc) { + fn insert(&mut self, name: impl Into, command: Arc) { let mut registry = self.registry.lock().unwrap(); registry.insert(name.into(), command); } @@ -73,7 +73,6 @@ impl CommandRegistry { #[derive(Clone)] pub struct Context { registry: CommandRegistry, - sinks: IndexMap>, crate source_map: SourceMap, crate host: Arc>, crate env: Arc>, @@ -87,57 +86,22 @@ impl Context { crate fn basic() -> Result> { Ok(Context { registry: CommandRegistry::new(), - sinks: indexmap::IndexMap::new(), source_map: SourceMap::new(), host: Arc::new(Mutex::new(crate::env::host::BasicHost)), env: Arc::new(Mutex::new(Environment::basic()?)), }) } - pub fn add_commands(&mut self, commands: Vec>) { + pub fn add_commands(&mut self, commands: Vec>) { for command in commands { self.registry.insert(command.name().to_string(), command); } } - pub fn add_sinks(&mut self, sinks: Vec>) { - for sink in sinks { - self.sinks.insert(sink.name().to_string(), sink); - } - } - pub fn add_span_source(&mut self, uuid: Uuid, span_source: SpanSource) { self.source_map.insert(uuid, span_source); } - crate fn has_sink(&self, name: &str) -> bool { - self.sinks.contains_key(name) - } - - crate fn get_sink(&self, name: &str) -> Arc { - self.sinks.get(name).unwrap().clone() - } - - crate fn run_sink( - &mut self, - command: Arc, - name_span: Option, - args: registry::EvaluatedArgs, - input: Vec>, - ) -> Result<(), ShellError> { - let command_args = SinkCommandArgs { - ctx: self.clone(), - call_info: CallInfo { - name_span, - source_map: self.source_map.clone(), - args, - }, - input, - }; - - command.run(command_args) - } - pub fn clone_commands(&self) -> CommandRegistry { self.registry.clone() } @@ -146,13 +110,13 @@ impl Context { self.registry.has(name) } - crate fn get_command(&self, name: &str) -> Arc { + crate fn get_command(&self, name: &str) -> Arc { self.registry.get_command(name).unwrap() } - crate fn run_command( + crate async fn run_command( &mut self, - command: Arc, + command: Arc, name_span: Option, source_map: SourceMap, args: hir::Call, @@ -161,7 +125,7 @@ impl Context { ) -> Result { let command_args = self.command_args(args, input, source, source_map, name_span); - command.run(command_args, self.registry()) + command.run(command_args, self.registry()).await } fn call_info( diff --git a/src/errors.rs b/src/errors.rs index 8d9f44fd97..1e2291aa5e 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -62,6 +62,15 @@ pub struct ShellError { cause: Option>, } +impl serde::de::Error for ShellError { + fn custom(msg: T) -> Self + where + T: std::fmt::Display, + { + ShellError::string(msg.to_string()) + } +} + impl ShellError { crate fn type_error( expected: impl Into, @@ -351,16 +360,6 @@ impl std::convert::From for ShellError { } } -impl std::convert::From for ShellError { - fn from(_input: futures_sink::VecSinkError) -> ShellError { - ProximateShellError::String(StringError { - title: format!("Unexpected Vec Sink Error"), - error: Value::nothing(), - }) - .start() - } -} - impl std::convert::From for ShellError { fn from(input: subprocess::PopenError) -> ShellError { ProximateShellError::String(StringError { diff --git a/src/evaluate/evaluator.rs b/src/evaluate/evaluator.rs index 563ccb1868..5507ebc2ac 100644 --- a/src/evaluate/evaluator.rs +++ b/src/evaluate/evaluator.rs @@ -55,6 +55,16 @@ crate fn evaluate_baseline_expr( )), } } + RawExpression::List(list) => { + let mut exprs = vec![]; + + for expr in list { + let expr = evaluate_baseline_expr(expr, registry, scope, source)?; + exprs.push(expr); + } + + Ok(Value::List(exprs).spanned(expr.span())) + } RawExpression::Block(block) => Ok(Spanned::from_item( Value::Block(Block::new(block.clone(), source.clone(), *expr.span())), expr.span(), diff --git a/src/lib.rs b/src/lib.rs index 813de129b5..919a809a91 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,11 +1,15 @@ #![feature(crate_visibility_modifier)] #![feature(in_band_lifetimes)] #![feature(async_await)] +#![feature(generators)] #![feature(try_trait)] #![feature(bind_by_move_pattern_guards)] #![feature(box_syntax)] #![feature(type_ascription)] +#![feature(core_intrinsics)] #![feature(option_flattening)] +#![feature(specialization)] +#![feature(proc_macro_hygiene)] #[macro_use] mod prelude; @@ -37,4 +41,4 @@ pub use cli::cli; pub use errors::ShellError; pub use object::base::{Primitive, Value}; pub use parser::parse::text::Text; -pub use parser::registry::{CommandConfig, EvaluatedArgs, NamedType, PositionalType}; +pub use parser::registry::{EvaluatedArgs, NamedType, PositionalType, Signature}; diff --git a/src/object.rs b/src/object.rs index 08f660fdae..f094c63a17 100644 --- a/src/object.rs +++ b/src/object.rs @@ -6,6 +6,6 @@ crate mod into; crate mod process; crate mod types; -crate use base::{Primitive, Value}; +crate use base::{Block, Primitive, Switch, Value}; crate use dict::{Dictionary, SpannedDictBuilder}; crate use files::dir_entry_dict; diff --git a/src/object/base.rs b/src/object/base.rs index 1a49f97a11..db534d46af 100644 --- a/src/object/base.rs +++ b/src/object/base.rs @@ -10,7 +10,7 @@ use chrono::{DateTime, Utc}; use chrono_humanize::Humanize; use derive_new::new; use ordered_float::OrderedFloat; -use serde::{ser::SerializeSeq, Deserialize, Deserializer, Serialize, Serializer}; +use serde::{Deserialize, Serialize}; use std::fmt; use std::path::PathBuf; use std::time::SystemTime; @@ -123,42 +123,13 @@ pub struct Operation { crate right: Value, } -#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Clone, new)] +#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Clone, Serialize, Deserialize, new)] pub struct Block { crate expressions: Vec, crate source: Text, crate span: Span, } -impl Serialize for Block { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - let mut seq = serializer.serialize_seq(None)?; - - let list = self - .expressions - .iter() - .map(|e| e.source(&self.source.clone())); - - for item in list { - seq.serialize_element(item.as_ref())?; - } - - seq.end() - } -} - -impl Deserialize<'de> for Block { - fn deserialize(_deserializer: D) -> Result - where - D: Deserializer<'de>, - { - unimplemented!("deserialize block") - } -} - impl Block { pub fn invoke(&self, value: &Spanned) -> Result, ShellError> { let scope = Scope::new(value.clone()); @@ -260,6 +231,7 @@ impl std::convert::TryFrom<&'a Spanned> for i64 { } } +#[derive(Serialize, Deserialize)] pub enum Switch { Present, Absent, diff --git a/src/object/config.rs b/src/object/config.rs index 0860feb94b..bffeff0ef0 100644 --- a/src/object/config.rs +++ b/src/object/config.rs @@ -1,12 +1,15 @@ +use crate::commands::from_toml::convert_toml_value_to_nu_value; +use crate::commands::to_toml::value_to_toml_value; use crate::errors::ShellError; +use crate::object::{Dictionary, Value}; use crate::prelude::*; use app_dirs::*; use indexmap::IndexMap; use log::trace; -use serde_derive::{Deserialize, Serialize}; +use serde::{Deserialize, Serialize}; use std::fs::{self, OpenOptions}; use std::io; -use std::path::Path; +use std::path::{Path, PathBuf}; const APP_INFO: AppInfo = AppInfo { name: "nu", @@ -16,7 +19,14 @@ const APP_INFO: AppInfo = AppInfo { #[derive(Deserialize, Serialize)] struct Config { #[serde(flatten)] - extra: IndexMap>, + extra: IndexMap, +} + +crate fn config_path() -> Result { + let location = app_root(AppDataType::UserConfig, &APP_INFO) + .map_err(|err| ShellError::string(&format!("Couldn't open config file:\n{}", err)))?; + + Ok(location.join("config.toml")) } crate fn write_config(config: &IndexMap>) -> Result<(), ShellError> { @@ -26,9 +36,9 @@ crate fn write_config(config: &IndexMap>) -> Result<(), S let filename = location.join("config.toml"); touch(&filename)?; - let contents = toml::to_string(&Config { - extra: config.iter().map(|(k, v)| (k.clone(), v.clone())).collect(), - })?; + let contents = value_to_toml_value(&Value::Object(Dictionary::new(config.clone()))); + + let contents = toml::to_string(&contents)?; fs::write(&filename, &contents)?; @@ -50,10 +60,18 @@ crate fn config(span: impl Into) -> Result .map(|v| v.spanned(span)) .map_err(|err| ShellError::string(&format!("Couldn't read config file:\n{}", err)))?; - let parsed: Config = toml::from_str(&contents) + let parsed: toml::Value = toml::from_str(&contents) .map_err(|err| ShellError::string(&format!("Couldn't parse config file:\n{}", err)))?; - Ok(parsed.extra) + let value = convert_toml_value_to_nu_value(&parsed, span); + + match value.item { + Value::Object(Dictionary { entries }) => Ok(entries), + other => Err(ShellError::type_error( + "Dictionary", + other.type_name().spanned(value.span), + )), + } } // A simple implementation of `% touch path` (ignores existing files) diff --git a/src/object/types.rs b/src/object/types.rs index 0264a33769..5ee3784d45 100644 --- a/src/object/types.rs +++ b/src/object/types.rs @@ -1,16 +1,9 @@ use crate::object::base as value; use crate::parser::hir; use crate::prelude::*; -use derive_new::new; -use serde_derive::Deserialize; +use log::trace; use std::path::PathBuf; -pub trait Type: std::fmt::Debug + Send { - type Extractor: ExtractType; - - fn name(&self) -> &'static str; -} - pub trait ExtractType: Sized { fn extract(value: &Spanned) -> Result; fn check(value: &'value Spanned) -> Result<&'value Spanned, ShellError>; @@ -19,8 +12,120 @@ pub trait ExtractType: Sized { } } +impl ExtractType for T { + default fn extract(_value: &Spanned) -> Result { + let name = unsafe { std::intrinsics::type_name::() }; + Err(ShellError::unimplemented(format!( + " ExtractType for {}", + name + ))) + } + + default fn check(_value: &'value Spanned) -> Result<&'value Spanned, ShellError> { + Err(ShellError::unimplemented("ExtractType for T")) + } + + default fn syntax_type() -> hir::SyntaxType { + hir::SyntaxType::Any + } +} + +impl ExtractType for Vec> { + fn extract(value: &Spanned) -> Result { + let name = unsafe { std::intrinsics::type_name::() }; + trace!(" Extracting {:?} for Vec<{}>", value, name); + + match value.item() { + Value::List(items) => { + let mut out = vec![]; + + for item in items { + out.push(T::extract(item)?.spanned(item.span)); + } + + Ok(out) + } + other => Err(ShellError::type_error( + "Vec", + other.type_name().spanned(value.span), + )), + } + } + + fn check(value: &'value Spanned) -> Result<&'value Spanned, ShellError> { + match value.item() { + Value::List(_) => Ok(value), + other => Err(ShellError::type_error( + "Vec", + other.type_name().spanned(value.span), + )), + } + } + + fn syntax_type() -> hir::SyntaxType { + hir::SyntaxType::List + } +} + +impl ExtractType for (T, U) { + fn extract(value: &Spanned) -> Result<(T, U), ShellError> { + let t_name = unsafe { std::intrinsics::type_name::() }; + let u_name = unsafe { std::intrinsics::type_name::() }; + + trace!("Extracting {:?} for ({}, {})", value, t_name, u_name); + + match value.item() { + Value::List(items) => { + if items.len() == 2 { + let first = &items[0]; + let second = &items[1]; + + Ok((T::extract(first)?, U::extract(second)?)) + } else { + Err(ShellError::type_error( + "two-element-tuple", + "not-two".spanned(value.span), + )) + } + } + other => Err(ShellError::type_error( + "two-element-tuple", + other.type_name().spanned(value.span), + )), + } + } +} + +impl ExtractType for Option { + fn extract(value: &Spanned) -> Result, ShellError> { + let name = unsafe { std::intrinsics::type_name::() }; + trace!("