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 {