diff --git a/.azure/azure-pipelines.yml b/.azure/azure-pipelines.yml index 9828066f3..4c160576b 100644 --- a/.azure/azure-pipelines.yml +++ b/.azure/azure-pipelines.yml @@ -1,5 +1,5 @@ variables: - lkg-rust-nightly: "2019-07-04" + lkg-rust-nightly: "2019-08-08" trigger: - master diff --git a/.gitignore b/.gitignore index 5a7ebb717..6d330b7f8 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/Cargo.lock b/Cargo.lock index 854cba43d..01ab86adb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -73,6 +73,16 @@ dependencies = [ "nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "async-trait" +version = "0.1.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.13 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.43 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "atty" version = "0.2.13" @@ -974,6 +984,26 @@ name = "futures" version = "0.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "futures-async-stream" +version = "0.1.0-alpha.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "futures-async-stream-macro 0.1.0-alpha.2 (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.2" +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.13 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.43 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "futures-channel-preview" version = "0.3.0-alpha.17" @@ -1864,6 +1894,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.7 (registry+https://github.com/rust-lang/crates.io-index)", "byte-unit 3.0.1 (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)", @@ -1880,6 +1911,7 @@ 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-async-stream 0.1.0-alpha.2 (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)", @@ -1916,7 +1948,6 @@ dependencies = [ "serde 1.0.98 (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.2 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.98 (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)", @@ -1925,6 +1956,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.1 (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)", @@ -2204,6 +2236,16 @@ dependencies = [ "ordermap 0.3.5 (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.13 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.43 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "pin-utils" version = "0.1.0-alpha.4" @@ -2356,6 +2398,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.60 (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.5.6" @@ -3052,6 +3106,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" @@ -3643,6 +3706,7 @@ dependencies = [ "checksum app_dirs 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e73a24bad9bd6a94d6395382a6c69fe071708ae4409f763c5475e14ee896313d" "checksum arrayref 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "0d382e583f07208808f6b1249e60848879ba3543f57c32277bf52d69c2f0f0ee" "checksum arrayvec 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)" = "b8d73f9beda665eaa98ab9e4f7442bd4e7de6652587de55b2525e52e29c1b0ba" +"checksum async-trait 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "fe9bad189e61411cfbcc8822b4a2b1534983ee24295fc8460d6be53da1afad74" "checksum atty 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)" = "1803c647a3ec87095e7ae7acfca019e98de5ec9a7d01343f611cf3152ed71a90" "checksum autocfg 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "22130e92352b948e7e82a49cdb0aa94f2211761117f29e052dd397c1ac33542b" "checksum backtrace 0.3.34 (registry+https://github.com/rust-lang/crates.io-index)" = "b5164d292487f037ece34ec0de2fcede2faa162f085dd96d2385ab81b12765ba" @@ -3745,6 +3809,8 @@ 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.28 (registry+https://github.com/rust-lang/crates.io-index)" = "45dc39533a6cae6da2b56da48edae506bb767ec07370f86f70fc062e9d435869" +"checksum futures-async-stream 0.1.0-alpha.2 (registry+https://github.com/rust-lang/crates.io-index)" = "11656e20f3c6e4a026757266461ffeb73d8c2fcbcc464fc6f9660d8d66452f32" +"checksum futures-async-stream-macro 0.1.0-alpha.2 (registry+https://github.com/rust-lang/crates.io-index)" = "bcb1eaa199281990734c14fdf5c22077ecd9f7fc2c24d7a37d796bd54dbb8f10" "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" @@ -3863,6 +3929,7 @@ dependencies = [ "checksum percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831" "checksum percent-encoding 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" "checksum petgraph 0.4.13 (registry+https://github.com/rust-lang/crates.io-index)" = "9c3659d1ee90221741f65dd128d9998311b0e40c5d3c23a62445938214abce4f" +"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.15 (registry+https://github.com/rust-lang/crates.io-index)" = "a7c1d2cfa5a714db3b5f24f0915e74fcdf91d09d496ba61329705dda7774d2af" "checksum platforms 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6cfec0daac55b13af394ceaaad095d17c790f77bdc9329264f06e49d6cd3206c" @@ -3879,6 +3946,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.13 (registry+https://github.com/rust-lang/crates.io-index)" = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1" +"checksum rand 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293" "checksum rand 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c618c47cd3ebd209790115ab837de41425723956ad3ce2e6a7f09890947cacb9" "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" @@ -3957,6 +4025,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.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ee7d12b854e48e680bf4b10856a7867e843845158fa8226e6c2f6cc38539cb01" +"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 a2352ccc8..33200621d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,16 +30,17 @@ ordered-float = {version = "1.0.2", features = ["serde"]} prettyprint = "0.7.0" 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 = "0.1.7" futures_codec = "0.2.5" term = "0.5.2" bytes = "0.4.12" log = "0.4.8" pretty_env_logger = "0.3.0" -serde = "1.0.98" +serde = { version = "1.0.98", features = ["derive"] } serde_json = "1.0.40" serde-hjson = "0.9.0" serde_yaml = "0.8" -serde_derive = "1.0.98" serde_bytes = "0.11.1" getset = "0.0.7" logos = "0.10.0-rc2" @@ -69,7 +70,7 @@ serde_ini = "0.2.0" subprocess = "0.1.18" sys-info = "0.5.7" mime = "0.3.13" -regex = "1.2.0" +regex = "1.2.1" pretty-hex = "0.1.0" neso = "0.5.0" rawkey = "0.1.2" @@ -85,6 +86,7 @@ heim = "0.0.6" [dev-dependencies] pretty_assertions = "0.6.1" +tempdir = "0.3.7" [lib] name = "nu" diff --git a/src/cli.rs b/src/cli.rs index 4fac51167..ff0deae11 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -1,20 +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::static_command; 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::registry; -use crate::parser::registry::CommandConfig; -use crate::parser::{Pipeline, PipelineElement, TokenNode}; +use crate::parser::registry::Signature; +use crate::parser::{hir, Pipeline, PipelineElement, TokenNode}; use crate::prelude::*; use log::{debug, trace}; @@ -62,8 +59,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) => { @@ -71,16 +67,16 @@ 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 { + } else { let fname = fname.to_string(); let name = params.name.clone(); - context.add_sinks(vec![Arc::new(PluginSink::new(name, fname, params))]); - Ok(()) - } else { + context.add_commands(vec![static_command(PluginSink::new( + name, fname, params, + ))]); Ok(()) } } @@ -149,18 +145,18 @@ pub async fn cli() -> Result<(), Box> { use crate::commands::*; context.add_commands(vec![ - command("ps", Box::new(ps::ps)), - command("ls", Box::new(ls::ls)), - 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("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("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("enter", Box::new(enter::enter)), command("n", Box::new(next::next)), command("p", Box::new(prev::prev)), @@ -179,23 +175,22 @@ pub async fn cli() -> Result<(), Box> { command("to-yaml", Box::new(to_yaml::to_yaml)), command("sort-by", Box::new(sort_by::sort_by)), command("tags", Box::new(tags::tags)), - Arc::new(Remove), - Arc::new(Copycp), - Arc::new(Open), - Arc::new(Mkdir), - Arc::new(Date), - Arc::new(Where), - Arc::new(Config), - Arc::new(Exit), - Arc::new(SkipWhile), - ]); - - context.add_sinks(vec![ - sink("autoview", Box::new(autoview::autoview)), - sink("clip", Box::new(clip::clip)), - sink("table", Box::new(table::table)), - sink("vtable", Box::new(vtable::vtable)), - Arc::new(Save), + static_command(Get), + //static_command(Cd), + static_command(Remove), + static_command(Open), + static_command(Where), + static_command(Config), + static_command(SkipWhile), + static_command(Exit), + static_command(Clip), + static_command(Autoview), + static_command(Copycp), + static_command(Date), + static_command(Mkdir), + static_command(Save), + static_command(Table), + static_command(VTable), ]); } let _ = load_plugins(&mut context); @@ -351,16 +346,18 @@ 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: Span::unknown(), - args: registry::Args { - positional: None, - named: None, - }, - })), + _ => pipeline + .commands + .push(ClassifiedCommand::Internal(InternalCommand { + command: static_command(autoview::Autoview), + name_span: Span::unknown(), + args: hir::Call::new( + Box::new(hir::Expression::synthetic_string("autoview")), + None, + None, + ), + })), } let mut input = ClassifiedInputStream::new(); @@ -388,35 +385,23 @@ async fn process_line(readline: Result, ctx: &mut Context ) } - (Some(ClassifiedCommand::Sink(SinkCommand { name_span, .. })), Some(_)) => { - return LineResult::Error(line.clone(), ShellError::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) { - return LineResult::Error(line.clone(), err); - } - break; - } - ( 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), } @@ -494,12 +479,11 @@ fn classify_command( match context.has_command(name) { true => { let command = context.get_command(name); - let config = command.config(); - let scope = Scope::empty(); + let config = command.signature(); 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, @@ -507,43 +491,28 @@ fn classify_command( args, })) } - false => match context.has_sink(name) { - true => { - let command = context.get_sink(name); - let config = command.config(); - let scope = Scope::empty(); + false => { + let arg_list_strings: Vec> = match call.children() { + //Some(args) => args.iter().map(|i| i.as_external_arg(source)).collect(), + Some(args) => args + .iter() + .filter_map(|i| match i { + TokenNode::Whitespace(_) => None, + other => Some(Tagged::from_simple_spanned_item( + other.as_external_arg(source), + other.span(), + )), + }) + .collect(), + None => vec![], + }; - let args = config.evaluate_args(call, context, &scope, source)?; - - Ok(ClassifiedCommand::Sink(SinkCommand { - command, - name_span: head.span().clone(), - args, - })) - } - false => { - let arg_list_strings: Vec> = match call.children() { - //Some(args) => args.iter().map(|i| i.as_external_arg(source)).collect(), - Some(args) => args - .iter() - .filter_map(|i| match i { - TokenNode::Whitespace(_) => None, - other => Some(Tagged::from_simple_spanned_item( - other.as_external_arg(source), - other.span(), - )), - }) - .collect(), - None => vec![], - }; - - Ok(ClassifiedCommand::External(ExternalCommand { - name: name.to_string(), - name_span: head.span().clone(), - args: arg_list_strings, - })) - } - }, + Ok(ClassifiedCommand::External(ExternalCommand { + name: name.to_string(), + name_span: head.span().clone(), + args: arg_list_strings, + })) + } } } diff --git a/src/commands.rs b/src/commands.rs index e9ef8b244..dfe815552 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -22,8 +22,8 @@ crate mod from_yaml; crate mod get; crate mod lines; crate mod ls; -crate mod next; crate mod mkdir; +crate mod next; crate mod open; crate mod pick; crate mod plugin; @@ -49,14 +49,23 @@ crate mod trim; crate mod vtable; crate mod where_; -crate use command::command; +crate use autoview::Autoview; +//crate use cd::Cd; +crate use clip::Clip; +crate use command::{ + command, static_command, Command, CommandArgs, RawCommandArgs, StaticCommand, + UnevaluatedCallInfo, +}; crate use config::Config; crate use cp::Copycp; crate use date::Date; crate use exit::Exit; -crate use open::Open; +crate use get::Get; crate use mkdir::Mkdir; +crate use open::Open; crate use rm::Remove; crate use save::Save; crate use skip_while::SkipWhile; +crate use table::Table; +crate use vtable::VTable; crate use where_::Where; diff --git a/src/commands/autoview.rs b/src/commands/autoview.rs index 0b9c55136..cfbcbe728 100644 --- a/src/commands/autoview.rs +++ b/src/commands/autoview.rs @@ -1,31 +1,67 @@ -use crate::commands::command::SinkCommandArgs; +use crate::commands::{RawCommandArgs, StaticCommand}; use crate::errors::ShellError; -use crate::format::GenericView; use crate::prelude::*; -pub fn autoview(args: SinkCommandArgs) -> Result<(), ShellError> { - if args.input.len() > 0 { - if let Tagged { - item: Value::Binary(_), - .. - } = args.input[0] - { - args.ctx.get_sink("binaryview").run(args)?; - } else if is_single_text_value(&args.input) { - args.ctx.get_sink("textview").run(args)?; - } else if equal_shapes(&args.input) { - args.ctx.get_sink("table").run(args)?; - } else { - let mut host = args.ctx.host.lock().unwrap(); - for i in args.input.iter() { - let view = GenericView::new(&i); - handle_unexpected(&mut *host, |host| crate::format::print_view(&view, host)); - host.stdout(""); - } - } +pub struct Autoview; + +#[derive(Deserialize)] +pub struct AutoviewArgs {} + +impl StaticCommand for Autoview { + fn name(&self) -> &str { + "autoview" } - Ok(()) + fn run( + &self, + args: CommandArgs, + registry: &CommandRegistry, + ) -> Result { + args.process_raw(registry, autoview)?.run() + } + + fn signature(&self) -> Signature { + Signature::build("autoview") + } +} + +pub fn autoview( + AutoviewArgs {}: AutoviewArgs, + mut context: RunnableContext, + raw: RawCommandArgs, +) -> Result { + Ok(OutputStream::new(async_stream_block! { + let input = context.input.drain_vec().await; + + if input.len() > 0 { + if let Tagged { + item: Value::Binary(_), + .. + } = input[0usize] + { + let binary = context.expect_command("binaryview"); + let result = binary.run(raw.with_input(input), &context.commands).await.unwrap(); + result.collect::>().await; + } else if is_single_text_value(&input) { + let text = context.expect_command("textview"); + let result = text.run(raw.with_input(input), &context.commands).await.unwrap(); + result.collect::>().await; + } else if equal_shapes(&input) { + let table = context.expect_command("table"); + let result = table.run(raw.with_input(input), &context.commands).await.unwrap(); + result.collect::>().await; + } else { + println!("TODO!") + // TODO + // let mut host = context.host.lock().unwrap(); + // for i in input.iter() { + // let view = GenericView::new(&i); + // handle_unexpected(&mut *host, |host| crate::format::print_view(&view, host)); + // host.stdout(""); + // } + } + } + })) } fn equal_shapes(input: &Vec>) -> bool { diff --git a/src/commands/cd.rs b/src/commands/cd.rs index ed14c8baf..0fe24add8 100644 --- a/src/commands/cd.rs +++ b/src/commands/cd.rs @@ -1,6 +1,8 @@ use crate::errors::ShellError; use crate::prelude::*; -pub fn cd(args: CommandArgs) -> Result { - args.shell_manager.cd(args.call_info, args.input) +pub fn cd(args: CommandArgs, registry: &CommandRegistry) -> Result { + let shell_manager = args.shell_manager.clone(); + let args = args.evaluate_once(registry)?; + shell_manager.cd(args) } diff --git a/src/commands/classified.rs b/src/commands/classified.rs index 69807d53a..cb02b674c 100644 --- a/src/commands/classified.rs +++ b/src/commands/classified.rs @@ -1,5 +1,5 @@ -use crate::commands::command::Sink; -use crate::parser::{registry::Args, TokenNode}; +use crate::commands::Command; +use crate::parser::{hir, TokenNode}; use crate::prelude::*; use bytes::{BufMut, BytesMut}; use futures::stream::StreamExt; @@ -81,7 +81,6 @@ crate enum ClassifiedCommand { #[allow(unused)] Expr(TokenNode), Internal(InternalCommand), - Sink(SinkCommand), External(ExternalCommand), } @@ -91,28 +90,15 @@ 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: Span, - crate args: Args, -} - -impl SinkCommand { - crate fn run(self, context: &mut Context, input: Vec>) -> Result<(), ShellError> { - context.run_sink(self.command, self.name_span.clone(), self.args, input) - } -} - crate struct InternalCommand { - crate command: Arc, + crate command: Arc, crate name_span: Span, - crate args: Args, + crate args: hir::Call, } impl InternalCommand { @@ -120,23 +106,27 @@ 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 = trace_stream!(target: "nu::trace_stream::internal", "input" = input.objects); - let result = context.run_command( - self.command, - self.name_span.clone(), - context.source_map.clone(), - self.args, - objects, - )?; + let result = context + .run_command( + self.command, + self.name_span.clone(), + context.source_map.clone(), + self.args, + source, + objects, + ) + .await?; let mut result = result.values; diff --git a/src/commands/clip.rs b/src/commands/clip.rs index 32294bf77..da88756d3 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") + } +} + +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).await; + }; + + let stream: BoxStream<'static, ReturnValue> = stream.boxed(); + + Ok(OutputStream::from(stream)) +} + +async fn inner_clip(input: Vec>, name: Span) -> 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.call_info.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 d83feebfc..8a871bea4 100644 --- a/src/commands/command.rs +++ b/src/commands/command.rs @@ -1,16 +1,51 @@ -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}; +use crate::parser::hir; +use crate::parser::{registry, ConfigDeserializer}; use crate::prelude::*; +use derive_new::new; 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: Span, +} + +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 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, Clone)] pub struct CallInfo { - pub args: Args, + pub args: registry::EvaluatedArgs, pub source_map: SourceMap, pub name_span: Span, } @@ -18,19 +53,264 @@ pub struct CallInfo { #[derive(Getters)] #[get = "crate"] pub struct CommandArgs { - pub host: Arc>, + pub host: Arc>, pub shell_manager: ShellManager, - pub call_info: CallInfo, + pub call_info: UnevaluatedCallInfo, + // pub host: Arc>, + // pub shell_manager: ShellManager, + // pub call_info: CallInfo, pub input: InputStream, } +#[derive(Getters)] +#[get = "crate"] +pub struct RawCommandArgs { + pub host: Arc>, + pub shell_manager: ShellManager, + pub call_info: UnevaluatedCallInfo, +} + +impl RawCommandArgs { + pub fn with_input(self, input: Vec>) -> CommandArgs { + CommandArgs { + host: self.host, + shell_manager: self.shell_manager, + call_info: self.call_info, + input: input.into(), + } + } +} + +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 nth(&self, pos: usize) -> Option<&Tagged> { - self.call_info.args.nth(pos) + pub fn evaluate_once( + self, + registry: ®istry::CommandRegistry, + ) -> Result { + let host = self.host.clone(); + let shell_manager = self.shell_manager.clone(); + let input = self.input; + let call_info = self.call_info.evaluate(registry, &Scope::empty())?; + + Ok(EvaluatedStaticCommandArgs::new( + host, + shell_manager, + call_info, + input, + )) } - pub fn positional_iter(&self) -> impl Iterator> { - self.call_info.args.positional_iter() + pub fn name_span(&self) -> Span { + self.call_info.name_span + } + + pub fn process<'de, T: Deserialize<'de>>( + self, + registry: &CommandRegistry, + callback: fn(T, RunnableContext) -> Result, + ) -> Result, ShellError> { + let shell_manager = self.shell_manager.clone(); + let source_map = self.call_info.source_map.clone(); + let host = self.host.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, + commands: registry.clone(), + shell_manager, + name: name_span, + source_map, + host, + }, + callback, + }) + } + + pub fn process_raw<'de, T: Deserialize<'de>>( + self, + registry: &CommandRegistry, + callback: fn(T, RunnableContext, RawCommandArgs) -> Result, + ) -> Result, ShellError> { + let raw_args = RawCommandArgs { + host: self.host.clone(), + shell_manager: self.shell_manager.clone(), + call_info: self.call_info.clone(), + }; + + let shell_manager = self.shell_manager.clone(); + let source_map = self.call_info.source_map.clone(); + let host = self.host.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(RunnableRawArgs { + args: T::deserialize(&mut deserializer)?, + context: RunnableContext { + input: input, + commands: registry.clone(), + shell_manager, + name: name_span, + source_map, + host, + }, + raw_args, + callback, + }) + } +} + +pub struct RunnableContext { + pub input: InputStream, + pub shell_manager: ShellManager, + pub host: Arc>, + pub commands: CommandRegistry, + pub source_map: SourceMap, + pub name: Span, +} + +impl RunnableContext { + pub fn cwd(&self) -> PathBuf { + PathBuf::from(self.shell_manager.path()) + } + + pub fn expect_command(&self, name: &str) -> Arc { + self.commands + .get_command(name) + .expect(&format!("Expected command {}", name)) + } +} + +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 RunnableRawArgs { + args: T, + raw_args: RawCommandArgs, + context: RunnableContext, + callback: fn(T, RunnableContext, RawCommandArgs) -> Result, +} + +impl RunnableRawArgs { + pub fn run(self) -> Result { + (self.callback)(self.args, self.context, self.raw_args) + } +} + +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>, + shell_manager: ShellManager, + call_info: CallInfo, + input: impl Into, + ) -> EvaluatedStaticCommandArgs { + EvaluatedStaticCommandArgs { + args: EvaluatedCommandArgs { + host, + shell_manager, + call_info, + }, + input: input.into(), + } + } + + pub fn name_span(&self) -> Span { + self.args.call_info.name_span + } + + pub fn parts(self) -> (InputStream, registry::EvaluatedArgs) { + let EvaluatedStaticCommandArgs { args, input } = self; + + (input, args.call_info.args) + } + + pub fn split(self) -> (InputStream, EvaluatedCommandArgs) { + let EvaluatedStaticCommandArgs { args, input } = self; + + (input, args) + } +} + +#[derive(Getters)] +#[get = "pub"] +pub struct EvaluatedFilterCommandArgs { + args: EvaluatedCommandArgs, + #[allow(unused)] + input: Tagged, +} + +impl Deref for EvaluatedFilterCommandArgs { + type Target = EvaluatedCommandArgs; + fn deref(&self) -> &Self::Target { + &self.args + } +} + +impl EvaluatedFilterCommandArgs { + pub fn new( + host: Arc>, + shell_manager: ShellManager, + call_info: CallInfo, + input: Tagged, + ) -> EvaluatedFilterCommandArgs { + EvaluatedFilterCommandArgs { + args: EvaluatedCommandArgs { + host, + shell_manager, + call_info, + }, + input, + } + } +} + +#[derive(Getters, new)] +#[get = "crate"] +pub struct EvaluatedCommandArgs { + pub host: Arc>, + pub shell_manager: ShellManager, + pub call_info: CallInfo, +} + +impl EvaluatedCommandArgs { + pub fn call_args(&self) -> ®istry::EvaluatedArgs { + &self.call_info.args + } + + pub fn nth(&self, pos: usize) -> Option<&Tagged> { + self.call_info.args.nth(pos) } pub fn expect_nth(&self, pos: usize) -> Result<&Tagged, ShellError> { @@ -45,18 +325,21 @@ impl CommandArgs { 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>, -} - #[derive(Debug, Serialize, Deserialize)] pub enum CommandAction { ChangePath(String), @@ -102,84 +385,156 @@ impl ReturnSuccess { } } -pub trait Command { - fn run(&self, args: CommandArgs) -> Result; +pub trait StaticCommand: Send + Sync { fn name(&self) -> &str; - fn config(&self) -> registry::CommandConfig { - registry::CommandConfig { + fn run( + &self, + args: CommandArgs, + registry: ®istry::CommandRegistry, + ) -> Result; + + 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 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), } } } -pub struct FnCommand { +#[allow(unused)] +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 StaticCommand for FnFilterCommand { fn name(&self) -> &str { &self.name } + + fn run( + &self, + args: CommandArgs, + registry: ®istry::CommandRegistry, + ) -> Result { + let CommandArgs { + host, + shell_manager, + call_info, + input, + } = args; + + let host: Arc> = host.clone(); + let shell_manager = shell_manager.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(), shell_manager.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 StaticCommand for FnRawCommand { + fn name(&self) -> &str { + &self.name + } + + fn run( + &self, + args: CommandArgs, + registry: ®istry::CommandRegistry, + ) -> Result { + (self.func)(args, registry) + } } pub fn command( name: &str, - func: Box Result>, -) -> Arc { - Arc::new(FnCommand { + func: Box< + dyn Fn(CommandArgs, ®istry::CommandRegistry) -> Result + + Send + + Sync, + >, +) -> Arc { + Arc::new(Command::Static(Arc::new(FnRawCommand { name: name.to_string(), func, - }) + }))) } -pub struct FnSink { - name: String, - func: Box Result<(), ShellError>>, +pub fn static_command(command: impl StaticCommand + 'static) -> Arc { + Arc::new(Command::Static(Arc::new(command))) } -impl Sink for FnSink { - fn run(&self, args: SinkCommandArgs) -> Result<(), ShellError> { - (self.func)(args) - } - - fn name(&self) -> &str { - &self.name - } -} - -pub fn sink( +#[allow(unused)] +pub fn filter( name: &str, - func: Box Result<(), ShellError>>, -) -> Arc { - Arc::new(FnSink { + func: fn(EvaluatedFilterCommandArgs) -> Result, +) -> Arc { + Arc::new(Command::Static(Arc::new(FnFilterCommand { name: name.to_string(), func, - }) + }))) } diff --git a/src/commands/config.rs b/src/commands/config.rs index 080e120e8..f185699aa 100644 --- a/src/commands/config.rs +++ b/src/commands/config.rs @@ -1,51 +1,60 @@ use crate::prelude::*; +use crate::commands::StaticCommand; 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 indexmap::IndexMap; -use log::trace; +use crate::parser::registry::{self}; use std::iter::FromIterator; pub struct Config; -impl Command for Config { - fn run(&self, args: CommandArgs) -> Result { - config(args) - } +#[derive(Deserialize)] +pub struct ConfigArgs { + set: Option<(Tagged, Tagged)>, + get: Option>, + clear: Tagged, + remove: Option>, + path: Tagged, +} + +impl StaticCommand for Config { 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); + fn signature(&self) -> Signature { + Signature::build("config") + .named("set", SyntaxType::Any) + .named("get", SyntaxType::Any) + .named("remove", SyntaxType::Any) + .switch("clear") + .switch("path") + } - 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, - } + fn run( + &self, + args: CommandArgs, + registry: ®istry::CommandRegistry, + ) -> Result { + args.process(registry, config)?.run() } } -pub fn config(args: CommandArgs) -> Result { - let mut result = crate::object::config::config(args.call_info.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_info.args.positional); - trace!("{:#?}", args.call_info.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)))?; @@ -55,34 +64,50 @@ pub fn config(args: CommandArgs) -> Result { ); } - if let Some(v) = args.get("set") { - if let Ok((key, value)) = v.as_pair() { - result.insert(key.as_string()?.to_string(), value.clone()); + if let Some((key, value)) = set { + result.insert(key.to_string(), value.clone()); - config::write_config(&result)?; + config::write_config(&result)?; - return Ok(stream![Tagged::from_simple_spanned_item( - Value::Object(result.into()), - v.span() - )] - .from_input_stream()); - } + return Ok(stream![Tagged::from_simple_spanned_item( + Value::Object(result.into()), + value.span() + )] + .from_input_stream()); } - if let Some(c) = args.get("clear") { + if let Tagged { + item: true, + tag: Tag { span, .. }, + } = clear + { result.clear(); config::write_config(&result)?; return Ok(stream![Tagged::from_simple_spanned_item( Value::Object(result.into()), - c.span() + span )] .from_input_stream()); } - if let Some(v) = args.get("remove") { - let key = v.as_string()?; + if let Tagged { + item: true, + tag: Tag { span, .. }, + } = path + { + let path = config::config_path()?; + + return Ok(stream![Tagged::from_simple_spanned_item( + Value::Primitive(Primitive::Path(path)), + span + )] + .from_input_stream()); + } + + if let Some(v) = remove { + let key = v.to_string(); if result.contains_key(&key) { result.remove(&key); @@ -93,15 +118,9 @@ pub fn config(args: CommandArgs) -> Result { ))); } - let obj = VecDeque::from_iter(vec![Value::Object(result.into()).simple_spanned(v)]); + let obj = VecDeque::from_iter(vec![Value::Object(result.into()).simple_spanned(v.span())]); return Ok(obj.from_input_stream()); } - if args.len() == 0 { - return Ok( - vec![Value::Object(result.into()).simple_spanned(args.call_info.name_span)].into(), - ); - } - - Err(ShellError::string(format!("Unimplemented"))) + return Ok(vec![Value::Object(result.into()).simple_spanned(name)].into()); } diff --git a/src/commands/cp.rs b/src/commands/cp.rs index de0f8129c..b8f03e584 100644 --- a/src/commands/cp.rs +++ b/src/commands/cp.rs @@ -1,33 +1,28 @@ use crate::errors::ShellError; use crate::parser::hir::SyntaxType; -use crate::parser::registry::{CommandConfig, NamedType, PositionalType}; +use crate::parser::registry::{CommandRegistry, Signature}; use crate::prelude::*; -use indexmap::IndexMap; use std::path::{Path, PathBuf}; pub struct Copycp; -impl Command for Copycp { - fn run(&self, args: CommandArgs) -> Result { - cp(args) +impl StaticCommand for Copycp { + fn run( + &self, + args: CommandArgs, + registry: &CommandRegistry, + ) -> Result { + cp(args, registry) } fn name(&self) -> &str { "cp" } - 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: false, - is_filter: false, - } + fn signature(&self) -> Signature { + Signature::build("cp") + .named("file", SyntaxType::Any) + .switch("recursive") } } @@ -100,10 +95,11 @@ impl FileStructure { } } -pub fn cp(args: CommandArgs) -> Result { +pub fn cp(args: CommandArgs, registry: &CommandRegistry) -> Result { let mut source = PathBuf::from(args.shell_manager.path()); let mut destination = PathBuf::from(args.shell_manager.path()); let name_span = args.call_info.name_span; + let args = args.evaluate_once(registry)?; match args .nth(0) diff --git a/src/commands/date.rs b/src/commands/date.rs index ea177459d..bbc6a77b5 100644 --- a/src/commands/date.rs +++ b/src/commands/date.rs @@ -3,34 +3,28 @@ use crate::object::{Dictionary, Value}; use crate::prelude::*; use chrono::{DateTime, Local, Utc}; -use crate::parser::registry::{CommandConfig, NamedType}; +use crate::commands::StaticCommand; +use crate::parser::registry::Signature; use chrono::{Datelike, TimeZone, Timelike}; use core::fmt::Display; use indexmap::IndexMap; pub struct Date; -impl Command for Date { - fn run(&self, args: CommandArgs) -> Result { - date(args) +impl StaticCommand for Date { + fn run( + &self, + args: CommandArgs, + registry: &CommandRegistry, + ) -> Result { + date(args, registry) } fn name(&self) -> &str { "date" } - fn config(&self) -> CommandConfig { - let mut named: IndexMap = IndexMap::new(); - named.insert("utc".to_string(), NamedType::Switch); - named.insert("local".to_string(), NamedType::Switch); - - CommandConfig { - name: self.name().to_string(), - positional: vec![], - rest_positional: false, - named, - is_sink: true, - is_filter: false, - } + fn signature(&self) -> Signature { + Signature::build("mkdir").switch("utc").switch("local") } } @@ -74,7 +68,9 @@ where Tagged::from_simple_spanned_item(Value::Object(Dictionary::from(indexmap)), span) } -pub fn date(args: CommandArgs) -> Result { +pub fn date(args: CommandArgs, registry: &CommandRegistry) -> Result { + let args = args.evaluate_once(registry)?; + let mut date_out = VecDeque::new(); let span = args.call_info.name_span; diff --git a/src/commands/enter.rs b/src/commands/enter.rs index 987a7115c..344134ffc 100644 --- a/src/commands/enter.rs +++ b/src/commands/enter.rs @@ -2,7 +2,9 @@ use crate::commands::command::CommandAction; use crate::errors::ShellError; use crate::prelude::*; -pub fn enter(args: CommandArgs) -> Result { +pub fn enter(args: CommandArgs, registry: &CommandRegistry) -> Result { + let args = args.evaluate_once(registry)?; + //TODO: We could also enter a value in the stream if args.len() == 0 { return Err(ShellError::labeled_error( diff --git a/src/commands/exit.rs b/src/commands/exit.rs index 6e33e3d7d..b695f4506 100644 --- a/src/commands/exit.rs +++ b/src/commands/exit.rs @@ -1,36 +1,31 @@ -use crate::commands::command::CommandAction; +use crate::commands::command::{CommandAction, StaticCommand}; use crate::errors::ShellError; -use crate::parser::registry::{CommandConfig, NamedType}; +use crate::parser::registry::{CommandRegistry, Signature}; use crate::prelude::*; -use indexmap::IndexMap; pub struct Exit; -impl Command for Exit { - fn run(&self, args: CommandArgs) -> Result { - exit(args) +impl StaticCommand for Exit { + fn run( + &self, + args: CommandArgs, + registry: &CommandRegistry, + ) -> Result { + exit(args, registry) } fn name(&self) -> &str { "exit" } - fn config(&self) -> CommandConfig { - let mut named: IndexMap = IndexMap::new(); - named.insert("now".to_string(), NamedType::Switch); - - CommandConfig { - name: self.name().to_string(), - positional: vec![], - rest_positional: false, - named, - is_sink: false, - is_filter: false, - } + fn signature(&self) -> Signature { + Signature::build("exit").switch("now") } } -pub fn exit(args: CommandArgs) -> Result { +pub fn exit(args: CommandArgs, registry: &CommandRegistry) -> Result { + let args = args.evaluate_once(registry)?; + if args.call_info.args.has("now") { Ok(vec![Ok(ReturnSuccess::Action(CommandAction::Exit))].into()) } else { diff --git a/src/commands/first.rs b/src/commands/first.rs index 96c820c3b..f34b9dba0 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::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 050534f76..1485646f9 100644 --- a/src/commands/from_csv.rs +++ b/src/commands/from_csv.rs @@ -45,9 +45,10 @@ pub fn from_csv_string_to_value( Ok(Tagged::from_item(Value::List(rows), tag)) } -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 d1a8fa446..e6b7e569a 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, tag)) } -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| { diff --git a/src/commands/from_json.rs b/src/commands/from_json.rs index eb118a3f2..2939f7f38 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, tag)) } -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| { diff --git a/src/commands/from_toml.rs b/src/commands/from_toml.rs index d6d39ede3..9594aece1 100644 --- a/src/commands/from_toml.rs +++ b/src/commands/from_toml.rs @@ -2,7 +2,7 @@ use crate::object::base::OF64; use crate::object::{Primitive, TaggedDictBuilder, Value}; use crate::prelude::*; -fn convert_toml_value_to_nu_value(v: &toml::Value, tag: impl Into) -> Tagged { +pub fn convert_toml_value_to_nu_value(v: &toml::Value, tag: impl Into) -> Tagged { let tag = tag.into(); match v { @@ -39,9 +39,14 @@ pub fn from_toml_string_to_value( Ok(convert_toml_value_to_nu_value(&v, tag)) } -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| { diff --git a/src/commands/from_xml.rs b/src/commands/from_xml.rs index f8e64bf6b..a2eebad62 100644 --- a/src/commands/from_xml.rs +++ b/src/commands/from_xml.rs @@ -56,9 +56,10 @@ pub fn from_xml_string_to_value( Ok(from_document_to_value(&parsed, tag)) } -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| { diff --git a/src/commands/from_yaml.rs b/src/commands/from_yaml.rs index b71ff59d7..7f3b0f13d 100644 --- a/src/commands/from_yaml.rs +++ b/src/commands/from_yaml.rs @@ -47,9 +47,13 @@ pub fn from_yaml_string_to_value( Ok(convert_yaml_value_to_nu_value(&v, tag)) } -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| { diff --git a/src/commands/get.rs b/src/commands/get.rs index 38d847156..873fd9f0d 100644 --- a/src/commands/get.rs +++ b/src/commands/get.rs @@ -1,8 +1,33 @@ +use crate::commands::StaticCommand; use crate::errors::ShellError; use crate::object::Value; use crate::prelude::*; -fn get_member(path: &str, span: Span, obj: &Tagged) -> 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: &Tagged, obj: &Tagged) -> Result, ShellError> { let mut current = obj; for p in path.split(".") { match current.get_data_by_key(p) { @@ -11,7 +36,7 @@ fn get_member(path: &str, span: Span, obj: &Tagged) -> Result) -> Result Result { - if args.len() == 0 { - return Err(ShellError::labeled_error( - "Get requires a field or field path", - "needs parameter", - args.call_info.name_span, - )); - } - - let amount = args.expect_nth(0)?.as_i64(); - +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(args - .input - .values - .skip(amount as u64) - .take(1) - .from_input_stream()); - } + // if let Some(amount) = amount { + // return Ok(input.values.skip(amount as u64).take(1).from_input_stream()); + // } - let fields: Result, _> = args - .positional_iter() - .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(); for field in &fields { - match get_member(&field.0, field.1, &item) { + match get_member(field, &item) { Ok(Tagged { item: Value::List(l), .. diff --git a/src/commands/lines.rs b/src/commands/lines.rs index 8999e1409..65cf7e06a 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 c591c1fb2..7a101d3d5 100644 --- a/src/commands/ls.rs +++ b/src/commands/ls.rs @@ -1,6 +1,8 @@ use crate::errors::ShellError; use crate::prelude::*; -pub fn ls(args: CommandArgs) -> Result { - args.shell_manager.ls(args.call_info, args.input) +pub fn ls(args: CommandArgs, registry: &CommandRegistry) -> Result { + let shell_manager = args.shell_manager.clone(); + let args = args.evaluate_once(registry)?; + shell_manager.ls(args) } diff --git a/src/commands/macros.rs b/src/commands/macros.rs index 44a88c189..8e148de5f 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)* ], @@ -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 ) } @@ -51,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, @@ -81,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)* ], @@ -101,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)* ], @@ -131,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)* ], @@ -151,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)* ], @@ -181,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)* ], @@ -201,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)* ], @@ -231,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)* ], @@ -254,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) @@ -286,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)* ], @@ -309,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() @@ -340,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![], @@ -376,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/mkdir.rs b/src/commands/mkdir.rs index 5dccb2810..ff62b2823 100644 --- a/src/commands/mkdir.rs +++ b/src/commands/mkdir.rs @@ -1,36 +1,32 @@ use crate::errors::ShellError; use crate::parser::hir::SyntaxType; -use crate::parser::registry::{CommandConfig, NamedType, PositionalType}; +use crate::parser::registry::{CommandRegistry, Signature}; use crate::prelude::*; -use indexmap::IndexMap; use std::path::{Path, PathBuf}; pub struct Mkdir; -impl Command for Mkdir { - fn run(&self, args: CommandArgs) -> Result { - mkdir(args) +impl StaticCommand for Mkdir { + fn run( + &self, + args: CommandArgs, + registry: &CommandRegistry, + ) -> Result { + mkdir(args, registry) } fn name(&self) -> &str { "mkdir" } - fn config(&self) -> CommandConfig { - let named: IndexMap = IndexMap::new(); - - CommandConfig { - name: self.name().to_string(), - positional: vec![PositionalType::mandatory("file", SyntaxType::Path)], - rest_positional: false, - named, - is_sink: false, - is_filter: false, - } + fn signature(&self) -> Signature { + Signature::build("mkdir").named("file", SyntaxType::Any) } } -pub fn mkdir(args: CommandArgs) -> Result { +pub fn mkdir(args: CommandArgs, registry: &CommandRegistry) -> Result { + let args = args.evaluate_once(registry)?; + let mut full_path = PathBuf::from(args.shell_manager.path()); match &args.nth(0) { diff --git a/src/commands/next.rs b/src/commands/next.rs index 5f0a714d1..17e952f63 100644 --- a/src/commands/next.rs +++ b/src/commands/next.rs @@ -2,6 +2,6 @@ use crate::commands::command::CommandAction; use crate::errors::ShellError; use crate::prelude::*; -pub fn next(_args: CommandArgs) -> Result { +pub fn next(_args: CommandArgs, _registry: &CommandRegistry) -> Result { Ok(vec![Ok(ReturnSuccess::Action(CommandAction::NextShell))].into()) } diff --git a/src/commands/open.rs b/src/commands/open.rs index fc754c6ab..4f6edf35f 100644 --- a/src/commands/open.rs +++ b/src/commands/open.rs @@ -1,62 +1,95 @@ +use crate::commands::StaticCommand; 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::registry::{self, Signature}; use crate::prelude::*; use mime::Mime; use std::path::{Path, PathBuf}; use std::str::FromStr; use uuid::Uuid; -command! { - Open as open(args, path: Tagged, --raw: Switch,) { - let span = args.call_info.name_span; +pub struct Open; - let cwd = args - .shell_manager.path(); +#[derive(Deserialize)] +pub struct OpenArgs { + path: Tagged, + raw: bool, +} - let full_path = PathBuf::from(cwd); +impl StaticCommand for Open { + fn name(&self) -> &str { + "open" + } - let path_str = path.to_str().ok_or(ShellError::type_error("Path", "invalid path".tagged(path.tag())))?; + fn signature(&self) -> Signature { + Signature::build(self.name()) + .required("path", SyntaxType::Path) + .switch("raw") + } - let (file_extension, contents, contents_tag, span_source) = fetch(&full_path, path_str, path.span())?; + fn run( + &self, + args: CommandArgs, + registry: ®istry::CommandRegistry, + ) -> Result { + args.process(registry, run)?.run() + } +} - let file_extension = if raw.is_present() { - None - } else { - file_extension - }; +fn run( + OpenArgs { raw, path }: OpenArgs, + RunnableContext { + shell_manager, + name, + .. + }: RunnableContext, +) -> Result { + let cwd = PathBuf::from(shell_manager.path()); + let full_path = PathBuf::from(cwd); - let mut stream = VecDeque::new(); + let path_str = path.to_str().ok_or(ShellError::type_error( + "Path", + "invalid path".tagged(path.tag()), + ))?; - if let Some(uuid) = contents_tag.origin { - // If we have loaded something, track its source - stream.push_back(ReturnSuccess::action(CommandAction::AddSpanSource(uuid, span_source))) + let (file_extension, contents, contents_tag, span_source) = + fetch(&full_path, path_str, path.span())?; + + let file_extension = if raw { None } else { file_extension }; + + let mut stream = VecDeque::new(); + + if let Some(uuid) = contents_tag.origin { + // 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_tag, name)?; + + match value { + Tagged { + 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_tag, - span, - )?; + other => stream.push_back(ReturnSuccess::value(other.tagged(contents_tag))), + }; - match value { - Tagged { 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.tagged(contents_tag))), - }; - - stream - } + Ok(stream.boxed().to_output_stream()) } pub fn fetch( diff --git a/src/commands/pick.rs b/src/commands/pick.rs index 2470b778c..71c019339 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::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 30633197e..41b8a83c6 100644 --- a/src/commands/plugin.rs +++ b/src/commands/plugin.rs @@ -1,3 +1,4 @@ +use crate::commands::StaticCommand; use crate::errors::ShellError; use crate::parser::registry; use crate::prelude::*; @@ -37,41 +38,34 @@ pub enum NuResult { pub struct PluginCommand { name: String, path: String, - config: registry::CommandConfig, + config: registry::Signature, } -impl Command for PluginCommand { - fn run(&self, args: CommandArgs) -> Result { - filter_plugin(self.path.clone(), args) - } +impl StaticCommand for PluginCommand { fn name(&self) -> &str { &self.name } - fn config(&self) -> registry::CommandConfig { + + fn signature(&self) -> registry::Signature { self.config.clone() } + + fn run( + &self, + args: CommandArgs, + registry: &CommandRegistry, + ) -> Result { + filter_plugin(self.path.clone(), args, registry) + } } -#[derive(new)] -pub struct PluginSink { - name: String, +pub fn filter_plugin( path: String, - config: registry::CommandConfig, -} + args: CommandArgs, + registry: &CommandRegistry, +) -> Result { + let args = args.evaluate_once(registry)?; -impl Sink for PluginSink { - fn run(&self, args: SinkCommandArgs) -> Result<(), ShellError> { - sink_plugin(self.path.clone(), args) - } - fn name(&self) -> &str { - &self.name - } - fn config(&self) -> registry::CommandConfig { - self.config.clone() - } -} - -pub fn filter_plugin(path: String, args: CommandArgs) -> Result { let mut child = std::process::Command::new(path) .stdin(std::process::Stdio::piped()) .stdout(std::process::Stdio::piped()) @@ -84,7 +78,7 @@ pub fn filter_plugin(path: String, args: CommandArgs) -> Result> = VecDeque::new(); eos.push_back(Value::Primitive(Primitive::EndOfStream).tagged_unknown()); - let call_info = args.call_info; + let call_info = args.call_info.clone(); let stream = bos .chain(args.input.values) @@ -238,20 +232,55 @@ pub fn filter_plugin(path: String, args: CommandArgs) -> Result Result<(), ShellError> { - //use subprocess::Exec; - let request = JsonRpc::new("sink", (args.call_info, args.input)); - let request_raw = serde_json::to_string(&request).unwrap(); - let mut tmpfile = tempfile::NamedTempFile::new()?; - let _ = writeln!(tmpfile, "{}", request_raw); - let _ = tmpfile.flush(); - - let mut child = std::process::Command::new(path) - .arg(tmpfile.path()) - .spawn() - .expect("Failed to spawn child process"); - - let _ = child.wait(); - - Ok(()) +#[derive(new)] +pub struct PluginSink { + name: String, + path: String, + config: registry::Signature, +} + +impl StaticCommand for PluginSink { + fn name(&self) -> &str { + &self.name + } + + fn signature(&self) -> registry::Signature { + self.config.clone() + } + + fn run( + &self, + args: CommandArgs, + registry: &CommandRegistry, + ) -> Result { + sink_plugin(self.path.clone(), args, registry) + } +} + +pub fn sink_plugin( + path: String, + args: CommandArgs, + registry: &CommandRegistry, +) -> Result { + //use subprocess::Exec; + let args = args.evaluate_once(registry)?; + let call_info = args.call_info.clone(); + + let stream = async_stream_block! { + let input: Vec> = args.input.values.collect().await; + + let request = JsonRpc::new("sink", (call_info.clone(), input)); + let request_raw = serde_json::to_string(&request).unwrap(); + let mut tmpfile = tempfile::NamedTempFile::new().unwrap(); + let _ = writeln!(tmpfile, "{}", request_raw); + let _ = tmpfile.flush(); + + let mut child = std::process::Command::new(path) + .arg(tmpfile.path()) + .spawn() + .expect("Failed to spawn child process"); + + let _ = child.wait(); + }; + Ok(OutputStream::new(stream)) } diff --git a/src/commands/prev.rs b/src/commands/prev.rs index 38f62b386..ea6e3c8d5 100644 --- a/src/commands/prev.rs +++ b/src/commands/prev.rs @@ -2,6 +2,6 @@ use crate::commands::command::CommandAction; use crate::errors::ShellError; use crate::prelude::*; -pub fn prev(_args: CommandArgs) -> Result { +pub fn prev(_args: CommandArgs, _registry: &CommandRegistry) -> Result { Ok(vec![Ok(ReturnSuccess::Action(CommandAction::PreviousShell))].into()) } diff --git a/src/commands/ps.rs b/src/commands/ps.rs index f684a8d79..3bac04688 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) -> 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/reject.rs b/src/commands/reject.rs index fd30247a8..443e6e372 100644 --- a/src/commands/reject.rs +++ b/src/commands/reject.rs @@ -2,20 +2,30 @@ use crate::errors::ShellError; use crate::object::base::reject_fields; use crate::prelude::*; -pub fn reject(args: CommandArgs) -> Result { - if args.len() == 0 { +pub fn reject(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::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 + let stream = input .values .map(move |item| reject_fields(&item, &fields, item.tag()).into_tagged_value()); diff --git a/src/commands/rm.rs b/src/commands/rm.rs index c57446bcf..c50ffd952 100644 --- a/src/commands/rm.rs +++ b/src/commands/rm.rs @@ -1,47 +1,46 @@ +use crate::commands::StaticCommand; use crate::errors::ShellError; use crate::parser::hir::SyntaxType; -use crate::parser::registry::{CommandConfig, NamedType, PositionalType}; use crate::prelude::*; use glob::glob; -use indexmap::IndexMap; use std::path::PathBuf; pub struct Remove; -impl Command for Remove { - fn run(&self, args: CommandArgs) -> Result { - rm(args) - } +#[derive(Deserialize)] +pub struct RemoveArgs { + path: Tagged, + recursive: bool, +} +impl StaticCommand for Remove { fn name(&self) -> &str { "rm" } - fn config(&self) -> CommandConfig { - let mut named: IndexMap = IndexMap::new(); - named.insert("recursive".to_string(), NamedType::Switch); + fn signature(&self) -> Signature { + Signature::build("rm") + .required("path", SyntaxType::Path) + .switch("recursive") + } - CommandConfig { - name: self.name().to_string(), - positional: vec![PositionalType::mandatory("file", SyntaxType::Path)], - rest_positional: false, - named, - is_sink: false, - is_filter: false, - } + fn run( + &self, + args: CommandArgs, + registry: &CommandRegistry, + ) -> Result { + args.process(registry, rm)?.run() } } -pub fn rm(args: CommandArgs) -> Result { - let mut full_path = PathBuf::from(args.shell_manager.path()); +pub fn rm( + RemoveArgs { path, recursive }: RemoveArgs, + context: RunnableContext, +) -> Result { + 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), } @@ -58,11 +57,11 @@ pub fn rm(args: CommandArgs) -> Result { match entry { Ok(path) => { if path.is_dir() { - if !args.has("recursive") { + if !recursive { return Err(ShellError::labeled_error( "is a directory", "is a directory", - args.call_info.name_span, + context.name, )); } std::fs::remove_dir_all(&path).expect("can not remove directory"); diff --git a/src/commands/save.rs b/src/commands/save.rs index 0b73e5898..aa32811b1 100644 --- a/src/commands/save.rs +++ b/src/commands/save.rs @@ -1,157 +1,215 @@ -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::registry::{CommandConfig, NamedType}; +use crate::object::Value; use crate::prelude::*; -use crate::SpanSource; -use indexmap::IndexMap; use std::path::{Path, PathBuf}; pub struct Save; -impl Sink for Save { - fn run(&self, args: SinkCommandArgs) -> Result<(), ShellError> { - save(args) - } +#[derive(Deserialize)] +pub struct SaveArgs { + path: Option>, + raw: bool, +} +impl StaticCommand for Save { fn name(&self) -> &str { "save" } - fn config(&self) -> CommandConfig { - let mut named: IndexMap = IndexMap::new(); - named.insert("raw".to_string(), NamedType::Switch); + fn signature(&self) -> Signature { + Signature::build("save") + .optional("path", SyntaxType::Path) + .switch("raw") + } - CommandConfig { - name: self.name().to_string(), - positional: vec![], - rest_positional: false, - named, - is_sink: false, - is_filter: false, - } + fn run( + &self, + args: CommandArgs, + registry: &CommandRegistry, + ) -> Result { + args.process(registry, save)?.run() } } -pub fn save(args: SinkCommandArgs) -> Result<(), ShellError> { - let cwd = args.ctx.shell_manager.path(); - let mut full_path = PathBuf::from(cwd); +pub fn save( + SaveArgs { + path, + raw: save_raw, + }: SaveArgs, + context: RunnableContext, +) -> Result { + let mut full_path = context.cwd(); - let save_raw = if args.call_info.args.has("raw") { - true - } else { - false - }; - - if args.call_info.args.positional.is_none() { - // If there is no filename, check the metadata for the origin filename - if args.input.len() > 0 { - let origin = args.input[0].origin(); - match origin.map(|x| args.call_info.source_map.get(&x)).flatten() { - Some(path) => match path { - SpanSource::File(file) => { - full_path.push(Path::new(file)); - } - _ => { - return Err(ShellError::labeled_error( + if path.is_none() { + let source_map = context.source_map.clone(); + let stream = async_stream_block! { + let input: Vec> = context.input.values.collect().await; + // If there is no filename, check the metadata for the origin filename + if input.len() > 0 { + let origin = input[0].origin(); + match origin.map(|x| source_map.get(&x)).flatten() { + Some(path) => match path { + SpanSource::File(file) => { + full_path.push(Path::new(file)); + } + _ => { + yield Err(ShellError::labeled_error( + "Save requires a filepath", + "needs path", + context.name, + )); + } + }, + None => { + yield Err(ShellError::labeled_error( "Save requires a filepath", "needs path", - args.call_info.name_span, + context.name, )); } - }, - None => { - return Err(ShellError::labeled_error( - "Save requires a filepath", - "needs path", - args.call_info.name_span, - )); } - } - } else { - return Err(ShellError::labeled_error( - "Save requires a filepath", - "needs path", - args.call_info.name_span, - )); - } - } else { - let arg = &args.call_info.args.positional.unwrap()[0]; - let arg_span = arg.span(); - match arg.item { - Value::Primitive(Primitive::String(ref s)) => full_path.push(Path::new(s)), - _ => { - return Err(ShellError::labeled_error( - "Save requires a string as a filepath", + } else { + yield Err(ShellError::labeled_error( + "Save requires a filepath", "needs path", - arg_span.clone(), + context.name, )); } - } - } - 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; + let contents = match full_path.extension() { + Some(x) if x == "csv" && !save_raw => { + if input.len() != 1 { + yield Err(ShellError::string( + "saving to csv requires a single object (or use --raw)", + )); } - save_data.push_str(&i.as_string().unwrap()); + to_csv_to_string(&value_to_csv_value(&input[0])).unwrap() } - } - save_data - } - }; + Some(x) if x == "toml" && !save_raw => { + if input.len() != 1 { + yield 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 { + yield 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 { + yield 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 { + yield 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); - Ok(()) + let _ = std::fs::write(full_path, contents); + }; + + Ok(OutputStream::new(stream)) + } else { + full_path.push(path.unwrap().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 { + yield 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 { + yield 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 { + yield 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 { + yield 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 { + yield 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); + }; + + Ok(OutputStream::new(stream)) + } } diff --git a/src/commands/shells.rs b/src/commands/shells.rs index 6fdc8a742..e97fd0a9e 100644 --- a/src/commands/shells.rs +++ b/src/commands/shells.rs @@ -2,7 +2,7 @@ use crate::errors::ShellError; use crate::object::TaggedDictBuilder; use crate::prelude::*; -pub fn shells(args: CommandArgs) -> Result { +pub fn shells(args: CommandArgs, _registry: &CommandRegistry) -> Result { let mut shells_out = VecDeque::new(); let span = args.call_info.name_span; diff --git a/src/commands/size.rs b/src/commands/size.rs index d4fc56de7..538662696 100644 --- a/src/commands/size.rs +++ b/src/commands/size.rs @@ -2,7 +2,7 @@ use crate::errors::ShellError; use crate::object::{TaggedDictBuilder, Value}; use crate::prelude::*; -pub fn size(args: CommandArgs) -> Result { +pub fn size(args: CommandArgs, _registry: &CommandRegistry) -> Result { let input = args.input; let span = args.call_info.name_span; Ok(input diff --git a/src/commands/skip_while.rs b/src/commands/skip_while.rs index 21a554a5f..c04befd55 100644 --- a/src/commands/skip_while.rs +++ b/src/commands/skip_while.rs @@ -1,44 +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 { - fn run(&self, args: CommandArgs) -> Result { - skip_while(args) - } +#[derive(Deserialize)] +pub struct SkipWhileArgs { + condition: value::Block, +} + +impl StaticCommand for SkipWhile { 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, - } + fn signature(&self) -> Signature { + Signature::build("skip-while") + .required("condition", SyntaxType::Block) + .filter() + } + + fn run( + &self, + args: CommandArgs, + registry: &CommandRegistry, + ) -> Result { + args.process(registry, skip_while)?.run() } } -pub fn skip_while(args: CommandArgs) -> Result { - if args.len() == 0 { - return Err(ShellError::labeled_error( - "Where requires a condition", - "needs condition", - args.call_info.name_span, - )); - } - - let block = args.nth(0).unwrap().as_block()?; - let input = args.input; - +pub fn skip_while( + SkipWhileArgs { condition }: SkipWhileArgs, + RunnableContext { input, .. }: RunnableContext, +) -> Result { 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/sort_by.rs b/src/commands/sort_by.rs index 2d7306e8a..38e240d28 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 c3bf3e3d1..68a9ba04e 100644 --- a/src/commands/split_column.rs +++ b/src/commands/split_column.rs @@ -3,20 +3,24 @@ use crate::object::{Primitive, TaggedDictBuilder, 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::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 acd898a28..3240cc5d5 100644 --- a/src/commands/split_row.rs +++ b/src/commands/split_row.rs @@ -3,20 +3,25 @@ use crate::object::{Primitive, Value}; 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::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/table.rs b/src/commands/table.rs index d8e3ca7f2..9e8a4bfbc 100644 --- a/src/commands/table.rs +++ b/src/commands/table.rs @@ -1,16 +1,41 @@ -use crate::commands::command::SinkCommandArgs; +use crate::commands::StaticCommand; use crate::errors::ShellError; use crate::format::TableView; use crate::prelude::*; +use futures_async_stream::async_stream_block; -pub fn table(args: SinkCommandArgs) -> Result<(), ShellError> { - if args.input.len() > 0 { - let mut host = args.ctx.host.lock().unwrap(); - let view = TableView::from_list(&args.input); - if let Some(view) = view { - handle_unexpected(&mut *host, |host| crate::format::print_view(&view, host)); - } +pub struct Table; + +#[derive(Deserialize)] +pub struct TableArgs {} + +impl StaticCommand for Table { + fn name(&self) -> &str { + "table" + } + fn run( + &self, + args: CommandArgs, + registry: &CommandRegistry, + ) -> Result { + args.process(registry, table)?.run() + } + fn signature(&self) -> Signature { + Signature::build("table") } - - Ok(()) +} + +pub fn table(_args: TableArgs, context: RunnableContext) -> Result { + let stream = async_stream_block! { + let input: Vec> = context.input.into_vec().await; + if input.len() > 0 { + let mut host = context.host.lock().unwrap(); + let view = TableView::from_list(&input); + if let Some(view) = view { + handle_unexpected(&mut *host, |host| crate::format::print_view(&view, host)); + } + } + }; + + Ok(OutputStream::new(stream)) } diff --git a/src/commands/tags.rs b/src/commands/tags.rs index 0b69ca41a..990ca252e 100644 --- a/src/commands/tags.rs +++ b/src/commands/tags.rs @@ -2,7 +2,7 @@ use crate::errors::ShellError; use crate::object::{TaggedDictBuilder, Value}; use crate::prelude::*; -pub fn tags(args: CommandArgs) -> Result { +pub fn tags(args: CommandArgs, _registry: &CommandRegistry) -> Result { let source_map = args.call_info.source_map.clone(); Ok(args .input diff --git a/src/commands/to_array.rs b/src/commands/to_array.rs index f6efef0d3..af031c62c 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) -> 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 017a4a249..04a99c5de 100644 --- a/src/commands/to_csv.rs +++ b/src/commands/to_csv.rs @@ -36,9 +36,11 @@ pub fn to_string(v: &Value) -> Result> { } } -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)) { diff --git a/src/commands/to_json.rs b/src/commands/to_json.rs index 75bd6085e..b52fa4cfd 100644 --- a/src/commands/to_json.rs +++ b/src/commands/to_json.rs @@ -41,9 +41,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 b228e243e..5020a9556 100644 --- a/src/commands/to_toml.rs +++ b/src/commands/to_toml.rs @@ -33,9 +33,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 a1b190ae5..aa1fba4fc 100644 --- a/src/commands/to_yaml.rs +++ b/src/commands/to_yaml.rs @@ -39,9 +39,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 754a076bb..1d3c76e90 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/vtable.rs b/src/commands/vtable.rs index 827170a2f..a60a87765 100644 --- a/src/commands/vtable.rs +++ b/src/commands/vtable.rs @@ -1,16 +1,41 @@ -use crate::commands::command::SinkCommandArgs; +use crate::commands::StaticCommand; use crate::errors::ShellError; use crate::format::VTableView; use crate::prelude::*; -pub fn vtable(args: SinkCommandArgs) -> Result<(), ShellError> { - if args.input.len() > 0 { - let mut host = args.ctx.host.lock().unwrap(); - let view = VTableView::from_list(&args.input); - if let Some(view) = view { - handle_unexpected(&mut *host, |host| crate::format::print_view(&view, host)); - } - } +pub struct VTable; - Ok(()) +#[derive(Deserialize)] +pub struct VTableArgs {} + +impl StaticCommand for VTable { + fn name(&self) -> &str { + "vtable" + } + fn run( + &self, + args: CommandArgs, + registry: &CommandRegistry, + ) -> Result { + args.process(registry, vtable)?.run() + } + fn signature(&self) -> Signature { + Signature::build("vtable") + } +} + +pub fn vtable(_args: VTableArgs, context: RunnableContext) -> Result { + let stream = async_stream_block! { + let input = context.input.into_vec().await; + + if input.len() > 0 { + let mut host = context.host.lock().unwrap(); + let view = VTableView::from_list(&input); + if let Some(view) = view { + handle_unexpected(&mut *host, |host| crate::format::print_view(&view, host)); + } + } + }; + + Ok(OutputStream::new(stream)) } diff --git a/src/commands/where_.rs b/src/commands/where_.rs index 0b7b818c8..b5f5aaa5e 100644 --- a/src/commands/where_.rs +++ b/src/commands/where_.rs @@ -1,14 +1,46 @@ +use crate::commands::StaticCommand; use crate::errors::ShellError; -use crate::object::Block; +use crate::object::base as value; +use crate::parser::hir::SyntaxType; +use crate::parser::registry; use crate::prelude::*; + use futures::future::ready; -use log::trace; +use serde::Deserialize; -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| { +#[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) + } + + fn run( + &self, + args: CommandArgs, + registry: ®istry::CommandRegistry, + ) -> Result { + args.process(registry, run)?.run() + } +} + +fn run( + WhereArgs { condition }: WhereArgs, + context: RunnableContext, +) -> Result { + Ok(context + .input + .values + .filter_map(move |item| { let result = condition.invoke(&item); let return_value = match result { @@ -17,7 +49,8 @@ command! { _ => None, }; - ready(return_value) + ready(return_value) }) - } + .boxed() + .to_output_stream()) } diff --git a/src/context.rs b/src/context.rs index 73c3b2891..b7fd14931 100644 --- a/src/context.rs +++ b/src/context.rs @@ -1,18 +1,20 @@ -use crate::commands::command::{CallInfo, Sink, SinkCommandArgs}; -use crate::parser::registry::{Args, CommandConfig, CommandRegistry}; +use crate::commands::{Command, UnevaluatedCallInfo}; +use crate::parser::hir; 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)] @@ -32,35 +34,67 @@ 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())), + } + } + + crate 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 registry = self.registry.lock().unwrap(); + // registry.keys().cloned().collect() + // } +} + #[derive(Clone)] pub struct Context { - commands: IndexMap>, - sinks: IndexMap>, + registry: CommandRegistry, crate source_map: SourceMap, crate host: Arc>, crate shell_manager: ShellManager, } impl Context { + crate fn registry(&self) -> &CommandRegistry { + &self.registry + } + crate fn basic() -> Result> { Ok(Context { - commands: indexmap::IndexMap::new(), - sinks: indexmap::IndexMap::new(), + registry: CommandRegistry::new(), source_map: SourceMap::new(), host: Arc::new(Mutex::new(crate::env::host::BasicHost)), shell_manager: ShellManager::basic()?, }) } - pub fn add_commands(&mut self, commands: Vec>) { + pub fn add_commands(&mut self, commands: Vec>) { for command in commands { - self.commands.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); + self.registry.insert(command.name().to_string(), command); } } @@ -68,67 +102,59 @@ impl Context { 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: Span, - args: Args, - 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() + // } 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() + 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: Span, 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()).await + } + + fn call_info( + &self, + args: hir::Call, + source: Text, + source_map: SourceMap, + name_span: Span, + ) -> UnevaluatedCallInfo { + UnevaluatedCallInfo { + args, + source, + source_map, + name_span, + } + } + + fn command_args( + &self, + args: hir::Call, + input: InputStream, + source: Text, + source_map: SourceMap, + name_span: Span, + ) -> CommandArgs { + CommandArgs { host: self.host.clone(), shell_manager: self.shell_manager.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 8033f86dd..3ae79849c 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/errors.rs b/src/errors.rs index bfa567024..18aa39bd5 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -56,6 +56,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, diff --git a/src/evaluate/evaluator.rs b/src/evaluate/evaluator.rs index 4f7add52f..0a73667d2 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: Tagged, #[new(default)] vars: IndexMap>, @@ -22,16 +22,24 @@ impl Scope { vars: IndexMap::new(), } } + + crate fn it_value(value: Tagged) -> 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).tagged_unknown()), RawExpression::Variable(var) => evaluate_reference(var, scope, source), RawExpression::Binary(binary) => { let left = evaluate_baseline_expr(binary.left(), registry, scope, source)?; @@ -48,6 +56,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).tagged(Tag::unknown_origin(expr.span()))) + } RawExpression::Block(block) => Ok(Tagged::from_simple_spanned_item( Value::Block(Block::new(block.clone(), source.clone(), expr.span())), expr.span(), diff --git a/src/format.rs b/src/format.rs index be835ea76..ae43c6f8a 100644 --- a/src/format.rs +++ b/src/format.rs @@ -7,6 +7,7 @@ crate mod vtable; use crate::prelude::*; crate use entries::EntriesView; +#[allow(unused)] crate use generic::GenericView; crate use table::TableView; crate use vtable::VTableView; diff --git a/src/format/table.rs b/src/format/table.rs index dcc4f5faa..1db0de12f 100644 --- a/src/format/table.rs +++ b/src/format/table.rs @@ -7,7 +7,7 @@ use prettytable::format::{FormatBuilder, LinePosition, LineSeparator}; use prettytable::{color, Attr, Cell, Row, Table}; -#[derive(new)] +#[derive(Debug, new)] pub struct TableView { headers: Vec, entries: Vec>, diff --git a/src/lib.rs b/src/lib.rs index ba0db8a6e..6386547c6 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; @@ -23,16 +27,19 @@ mod parser; mod plugin; mod shell; mod stream; +mod traits; +mod utils; pub use crate::commands::command::{CallInfo, ReturnSuccess, ReturnValue}; pub use crate::context::{SourceMap, SpanSource}; pub use crate::env::host::BasicHost; pub use crate::object::base::OF64; 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}; pub use object::dict::{Dictionary, TaggedDictBuilder}; pub use object::meta::{Span, Tag, Tagged, TaggedItem}; pub use parser::parse::text::Text; -pub use parser::registry::{Args, CommandConfig, NamedType, PositionalType}; +pub use parser::registry::{EvaluatedArgs, NamedType, PositionalType, Signature}; diff --git a/src/object.rs b/src/object.rs index d0424f19d..8cc86d456 100644 --- a/src/object.rs +++ b/src/object.rs @@ -7,6 +7,7 @@ crate mod meta; crate mod process; crate mod types; +#[allow(unused)] crate use base::{Block, Primitive, Switch, Value}; crate use dict::{Dictionary, TaggedDictBuilder}; crate use files::dir_entry_dict; diff --git a/src/object/base.rs b/src/object/base.rs index 92e1ec4c8..2da89c181 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::TaggedDictBuilder; @@ -9,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; @@ -127,42 +128,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: &Tagged) -> Result, ShellError> { let scope = Scope::new(value.clone()); @@ -174,7 +146,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()) @@ -259,12 +236,14 @@ impl std::convert::TryFrom<&'a Tagged> for i64 { } } +#[derive(Serialize, Deserialize)] pub enum Switch { Present, Absent, } impl Switch { + #[allow(unused)] pub fn is_present(&self) -> bool { match self { Switch::Present => true, @@ -507,15 +486,15 @@ impl Value { } } - crate fn as_pair(&self) -> Result<(Tagged, Tagged), ShellError> { - match self { - Value::List(list) if list.len() == 2 => Ok((list[0].clone(), list[1].clone())), - other => Err(ShellError::string(format!( - "Expected pair, got {:?}", - other - ))), - } - } + // crate fn as_pair(&self) -> Result<(Tagged, Tagged), ShellError> { + // match self { + // Value::List(list) if list.len() == 2 => Ok((list[0].clone(), list[1].clone())), + // other => Err(ShellError::string(format!( + // "Expected pair, got {:?}", + // other + // ))), + // } + // } crate fn as_string(&self) -> Result { match self { @@ -544,17 +523,6 @@ impl Value { } } - crate fn as_block(&self) -> Result { - match self { - Value::Block(block) => Ok(block.clone()), - // TODO: this should definitely be more general with better errors - other => Err(ShellError::string(format!( - "Expected block, got {:?}", - other - ))), - } - } - crate fn is_true(&self) -> bool { match self { Value::Primitive(Primitive::Boolean(true)) => true, diff --git a/src/object/config.rs b/src/object/config.rs index b46c6d701..77c974e79 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", @@ -19,6 +22,13 @@ struct Config { 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> { let location = app_root(AppDataType::UserConfig, &APP_INFO) .map_err(|err| ShellError::string(&format!("Couldn't open config file:\n{}", err)))?; @@ -26,9 +36,9 @@ crate fn write_config(config: &IndexMap>) -> Result<(), Sh 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.simple_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, Tag::unknown_origin(span)); + let tag = value.tag(); + match value.item { + Value::Object(Dictionary { entries }) => Ok(entries), + other => Err(ShellError::type_error( + "Dictionary", + other.type_name().tagged(tag), + )), + } } // A simple implementation of `% touch path` (ignores existing files) diff --git a/src/object/meta.rs b/src/object/meta.rs index 4881d7d00..6475ba1e8 100644 --- a/src/object/meta.rs +++ b/src/object/meta.rs @@ -1,8 +1,9 @@ +use crate::prelude::*; use crate::Text; use derive_new::new; use getset::Getters; +use serde::Deserialize; use serde::Serialize; -use serde_derive::Deserialize; use uuid::Uuid; #[derive(new, Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Serialize, Deserialize, Hash)] @@ -11,6 +12,12 @@ pub struct Tagged { pub item: T, } +impl HasSpan for Tagged { + fn span(&self) -> Span { + self.tag.span + } +} + pub trait TaggedItem: Sized { fn tagged(self, tag: impl Into) -> Tagged { Tagged::from_item(self, tag.into()) diff --git a/src/object/types.rs b/src/object/types.rs index 6cdc0d7d2..8177f23ff 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: &Tagged) -> Result; fn check(value: &'value Tagged) -> Result<&'value Tagged, ShellError>; @@ -19,8 +12,120 @@ pub trait ExtractType: Sized { } } +impl ExtractType for T { + default fn extract(_value: &Tagged) -> Result { + let name = std::intrinsics::type_name::(); + Err(ShellError::unimplemented(format!( + " ExtractType for {}", + name + ))) + } + + default fn check(_value: &'value Tagged) -> Result<&'value Tagged, ShellError> { + Err(ShellError::unimplemented("ExtractType for T")) + } + + default fn syntax_type() -> hir::SyntaxType { + hir::SyntaxType::Any + } +} + +impl ExtractType for Vec> { + fn extract(value: &Tagged) -> Result { + let name = 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)?.tagged(item.tag())); + } + + Ok(out) + } + other => Err(ShellError::type_error( + "Vec", + other.type_name().tagged(value.tag()), + )), + } + } + + fn check(value: &'value Tagged) -> Result<&'value Tagged, ShellError> { + match value.item() { + Value::List(_) => Ok(value), + other => Err(ShellError::type_error( + "Vec", + other.type_name().tagged(value.tag()), + )), + } + } + + fn syntax_type() -> hir::SyntaxType { + hir::SyntaxType::List + } +} + +impl ExtractType for (T, U) { + fn extract(value: &Tagged) -> Result<(T, U), ShellError> { + let t_name = std::intrinsics::type_name::(); + let u_name = 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".tagged(value.tag()), + )) + } + } + other => Err(ShellError::type_error( + "two-element-tuple", + other.type_name().tagged(value.tag()), + )), + } + } +} + +impl ExtractType for Option { + fn extract(value: &Tagged) -> Result, ShellError> { + let name = std::intrinsics::type_name::(); + trace!("