diff --git a/.azure/azure-pipelines.yml b/.azure/azure-pipelines.yml index 06f5dbdaa2..64f98ae6db 100644 --- a/.azure/azure-pipelines.yml +++ b/.azure/azure-pipelines.yml @@ -6,6 +6,9 @@ strategy: linux-stable: image: ubuntu-18.04 style: 'unflagged' + linux-minimal: + image: ubuntu-18.04 + style: 'minimal' macos-stable: image: macos-10.14 style: 'unflagged' @@ -58,6 +61,9 @@ steps: - bash: NUSHELL_ENABLE_ALL_FLAGS=1 RUSTFLAGS="-D warnings" cargo clippy --all --features=stable -- -D clippy::result_unwrap_used -D clippy::option_unwrap_used condition: eq(variables['style'], 'canary') displayName: Check clippy lints + - bash: RUSTFLAGS="-D warnings" cargo test --all --no-default-features + condition: eq(variables['style'], 'minimal') + displayName: Run tests - bash: cargo fmt --all -- --check condition: eq(variables['style'], 'fmt') displayName: Lint diff --git a/Cargo.lock b/Cargo.lock index d0cc3ba7c6..4fbb184fed 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2464,6 +2464,8 @@ dependencies = [ "nu-value-ext", "nu_plugin_binaryview", "nu_plugin_fetch", + "nu_plugin_from_bson", + "nu_plugin_from_sqlite", "nu_plugin_inc", "nu_plugin_match", "nu_plugin_post", @@ -2471,6 +2473,8 @@ dependencies = [ "nu_plugin_start", "nu_plugin_sys", "nu_plugin_textview", + "nu_plugin_to_bson", + "nu_plugin_to_sqlite", "nu_plugin_tree", "pretty_env_logger", "semver 0.10.0", @@ -2501,7 +2505,6 @@ dependencies = [ "async-trait", "base64 0.12.3", "bigdecimal", - "bson", "byte-unit", "bytes 0.5.5", "calamine", @@ -2752,6 +2755,37 @@ dependencies = [ "url", ] +[[package]] +name = "nu_plugin_from_bson" +version = "0.16.1" +dependencies = [ + "bigdecimal", + "bson", + "nu-build", + "nu-errors", + "nu-plugin", + "nu-protocol", + "nu-source", + "nu-value-ext", + "num-traits 0.2.12", +] + +[[package]] +name = "nu_plugin_from_sqlite" +version = "0.16.1" +dependencies = [ + "bigdecimal", + "nu-build", + "nu-errors", + "nu-plugin", + "nu-protocol", + "nu-source", + "nu-value-ext", + "num-traits 0.2.12", + "rusqlite", + "tempfile", +] + [[package]] name = "nu_plugin_inc" version = "0.16.1" @@ -2857,6 +2891,36 @@ dependencies = [ "url", ] +[[package]] +name = "nu_plugin_to_bson" +version = "0.16.1" +dependencies = [ + "bson", + "nu-build", + "nu-errors", + "nu-plugin", + "nu-protocol", + "nu-source", + "nu-value-ext", + "num-traits 0.2.12", +] + +[[package]] +name = "nu_plugin_to_sqlite" +version = "0.16.1" +dependencies = [ + "hex 0.4.2", + "nu-build", + "nu-errors", + "nu-plugin", + "nu-protocol", + "nu-source", + "nu-value-ext", + "num-traits 0.2.12", + "rusqlite", + "tempfile", +] + [[package]] name = "nu_plugin_tree" version = "0.16.1" diff --git a/Cargo.toml b/Cargo.toml index b9d25ad70d..ecd54bb507 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,6 +27,8 @@ nu-source = {version = "0.16.1", path = "./crates/nu-source"} nu-value-ext = {version = "0.16.1", path = "./crates/nu-value-ext"} nu_plugin_binaryview = {version = "0.16.1", path = "./crates/nu_plugin_binaryview", optional = true} nu_plugin_fetch = {version = "0.16.1", path = "./crates/nu_plugin_fetch", optional = true} +nu_plugin_from_bson = {version = "0.16.1", path = "./crates/nu_plugin_from_bson", optional = true} +nu_plugin_from_sqlite = {version = "0.16.1", path = "./crates/nu_plugin_from_sqlite", optional = true} nu_plugin_inc = {version = "0.16.1", path = "./crates/nu_plugin_inc", optional = true} nu_plugin_match = {version = "0.16.1", path = "./crates/nu_plugin_match", optional = true} nu_plugin_post = {version = "0.16.1", path = "./crates/nu_plugin_post", optional = true} @@ -34,6 +36,8 @@ nu_plugin_ps = {version = "0.16.1", path = "./crates/nu_plugin_ps", optional = t nu_plugin_start = {version = "0.16.1", path = "./crates/nu_plugin_start", optional = true} nu_plugin_sys = {version = "0.16.1", path = "./crates/nu_plugin_sys", optional = true} nu_plugin_textview = {version = "0.16.1", path = "./crates/nu_plugin_textview", optional = true} +nu_plugin_to_bson = {version = "0.16.1", path = "./crates/nu_plugin_to_bson", optional = true} +nu_plugin_to_sqlite = {version = "0.16.1", path = "./crates/nu_plugin_to_sqlite", optional = true} nu_plugin_tree = {version = "0.16.1", path = "./crates/nu_plugin_tree", optional = true} crossterm = {version = "0.17.5", optional = true} @@ -58,8 +62,20 @@ serde = {version = "1.0.110", features = ["derive"]} toml = "0.5.6" [features] -default = ["sys", "ps", "textview", "inc"] -stable = ["default", "binaryview", "match", "tree", "post", "fetch", "clipboard-cli", "trash-support", "start"] +default = [ + "sys", + "ps", + "textview", + "inc", + "git-support", + "directories-support", + "ctrlc-support", + "which-support", + "ptree-support", + "term-support", + "uuid-support", +] +stable = ["default", "binaryview", "match", "tree", "post", "fetch", "clipboard-cli", "trash-support", "start", "starship-prompt", "bson", "sqlite"] # Default inc = ["semver", "nu_plugin_inc"] @@ -69,16 +85,25 @@ textview = ["crossterm", "syntect", "url", "nu_plugin_textview"] # Stable binaryview = ["nu_plugin_binaryview"] +bson = ["nu_plugin_from_bson", "nu_plugin_to_bson"] fetch = ["nu_plugin_fetch"] match = ["nu_plugin_match"] post = ["nu_plugin_post"] +sqlite = ["nu_plugin_from_sqlite", "nu_plugin_to_sqlite"] start = ["nu_plugin_start"] trace = ["nu-parser/trace"] tree = ["nu_plugin_tree"] clipboard-cli = ["nu-cli/clipboard-cli"] -# starship-prompt = ["nu-cli/starship-prompt"] +ctrlc-support = ["nu-cli/ctrlc"] +directories-support = ["nu-cli/directories", "nu-cli/dirs"] +git-support = ["nu-cli/git2"] +ptree-support = ["nu-cli/ptree"] +starship-prompt = ["nu-cli/starship-prompt"] +term-support = ["nu-cli/term"] trash-support = ["nu-cli/trash-support"] +uuid-support = ["nu-cli/uuid_crate"] +which-support = ["nu-cli/ichwh", "nu-cli/which"] # Core plugins that ship with `cargo install nu` by default # Currently, Cargo limits us to installing only one binary diff --git a/crates/nu-cli/Cargo.toml b/crates/nu-cli/Cargo.toml index 6b8434854c..c7307c9a9b 100644 --- a/crates/nu-cli/Cargo.toml +++ b/crates/nu-cli/Cargo.toml @@ -25,7 +25,6 @@ async-recursion = "0.3.1" async-trait = "0.1.36" base64 = "0.12.3" bigdecimal = {version = "0.1.2", features = ["serde"]} -bson = {version = "0.14.1", features = ["decimal128"]} byte-unit = "3.1.3" bytes = "0.5.5" calamine = "0.16" @@ -33,10 +32,10 @@ chrono = {version = "0.4.11", features = ["serde"]} clap = "2.33.1" codespan-reporting = "0.9.5" csv = "1.1" -ctrlc = "3.1.4" +ctrlc = {version = "3.1.4", optional = true} derive-new = "0.5.8" -directories = "2.0.2" -dirs = "2.0.2" +directories = {version = "2.0.2", optional = true} +dirs = {version = "2.0.2", optional = true} dunce = "1.0.1" eml-parser = "0.1.0" filesize = "0.2.0" @@ -44,12 +43,12 @@ futures = {version = "0.3", features = ["compat", "io-compat"]} futures-util = "0.3.5" futures_codec = "0.4" getset = "0.1.1" -git2 = {version = "0.13.6", default_features = false} +git2 = {version = "0.13.6", default_features = false, optional = true} glob = "0.3.0" hex = "0.4" htmlescape = "0.3.1" ical = "0.6.*" -ichwh = "0.3.4" +ichwh = {version = "0.3.4", optional = true} indexmap = {version = "1.4.0", features = ["serde-1"]} itertools = "0.9.0" log = "0.4.8" @@ -62,7 +61,7 @@ parking_lot = "0.11.0" pin-utils = "0.1.0" pretty-hex = "0.1.1" pretty_env_logger = "0.4.0" -ptree = {version = "0.2"} +ptree = {version = "0.2", optional = true} query_interface = "0.3.5" rand = "0.7" regex = "1" @@ -79,20 +78,20 @@ sha2 = "0.9.1" shellexpand = "2.0.0" strip-ansi-escapes = "0.1.0" tempfile = "3.1.0" -term = "0.5.2" +term = {version = "0.5.2", optional = true} term_size = "0.3.2" termcolor = "1.1.0" toml = "0.5.6" typetag = "0.1.5" umask = "1.0.0" unicode-xid = "0.2.1" -uuid_crate = {package = "uuid", version = "0.8.1", features = ["v4"]} -which = "4.0.1" +uuid_crate = {package = "uuid", version = "0.8.1", features = ["v4"], optional = true} +which = {version = "4.0.1", optional = true} clipboard = {version = "0.5", optional = true} encoding_rs = "0.8.23" rayon = "1.3.1" -starship = "0.43.0" +starship = {version = "0.43.0", optional = true} trash = {version = "1.0.1", optional = true} [target.'cfg(unix)'.dependencies] @@ -106,6 +105,7 @@ users = "0.10.0" [dependencies.rusqlite] features = ["bundled", "blob"] +optional = true version = "0.23.1" [build-dependencies] @@ -116,7 +116,7 @@ quickcheck = "0.9" quickcheck_macros = "0.9" [features] -stable = [] -# starship-prompt = ["starship"] clipboard-cli = ["clipboard"] +stable = [] +starship-prompt = ["starship"] trash-support = ["trash"] diff --git a/crates/nu-cli/src/cli.rs b/crates/nu-cli/src/cli.rs index 8762b07611..4e06cd69be 100644 --- a/crates/nu-cli/src/cli.rs +++ b/crates/nu-cli/src/cli.rs @@ -12,6 +12,8 @@ use futures_codec::FramedRead; use nu_errors::{ProximateShellError, ShellDiagnostic, ShellError}; use nu_protocol::hir::{ClassifiedCommand, Expression, InternalCommand, Literal, NamedArguments}; use nu_protocol::{Primitive, ReturnSuccess, Signature, UntaggedValue, Value}; +#[allow(unused)] +use nu_source::Tagged; use log::{debug, trace}; use rustyline::error::ReadlineError; @@ -374,12 +376,9 @@ pub fn create_default_context( whole_stream_command(MathVariance), // File format output whole_stream_command(To), - whole_stream_command(ToBSON), whole_stream_command(ToCSV), whole_stream_command(ToHTML), whole_stream_command(ToJSON), - whole_stream_command(ToSQLite), - whole_stream_command(ToDB), whole_stream_command(ToMarkdown), whole_stream_command(ToTOML), whole_stream_command(ToTSV), @@ -392,11 +391,8 @@ pub fn create_default_context( whole_stream_command(FromTSV), whole_stream_command(FromSSV), whole_stream_command(FromINI), - whole_stream_command(FromBSON), whole_stream_command(FromJSON), whole_stream_command(FromODS), - whole_stream_command(FromDB), - whole_stream_command(FromSQLite), whole_stream_command(FromTOML), whole_stream_command(FromURL), whole_stream_command(FromXLSX), @@ -411,6 +407,7 @@ pub fn create_default_context( whole_stream_command(Random), whole_stream_command(RandomBool), whole_stream_command(RandomDice), + #[cfg(feature = "uuid_crate")] whole_stream_command(RandomUUID), ]); @@ -432,15 +429,18 @@ pub async fn run_vec_of_pipelines( let _ = crate::load_plugins(&mut context); - let cc = context.ctrl_c.clone(); + #[cfg(feature = "ctrlc")] + { + let cc = context.ctrl_c.clone(); - ctrlc::set_handler(move || { - cc.store(true, Ordering::SeqCst); - }) - .expect("Error setting Ctrl-C handler"); + ctrlc::set_handler(move || { + cc.store(true, Ordering::SeqCst); + }) + .expect("Error setting Ctrl-C handler"); - if context.ctrl_c.load(Ordering::SeqCst) { - context.ctrl_c.store(false, Ordering::SeqCst); + if context.ctrl_c.load(Ordering::SeqCst) { + context.ctrl_c.store(false, Ordering::SeqCst); + } } // before we start up, let's run our startup commands @@ -557,11 +557,15 @@ pub async fn cli( // we are ok if history does not exist let _ = rl.load_history(&History::path()); - let cc = context.ctrl_c.clone(); - ctrlc::set_handler(move || { - cc.store(true, Ordering::SeqCst); - }) - .expect("Error setting Ctrl-C handler"); + #[cfg(feature = "ctrlc")] + { + let cc = context.ctrl_c.clone(); + + ctrlc::set_handler(move || { + cc.store(true, Ordering::SeqCst); + }) + .expect("Error setting Ctrl-C handler"); + } let mut ctrlcbreak = false; // before we start up, let's run our startup commands @@ -662,21 +666,35 @@ pub async fn cli( let colored_prompt = { if use_starship { - std::env::set_var("STARSHIP_SHELL", ""); - std::env::set_var("PWD", &cwd); - let mut starship_context = - starship::context::Context::new_with_dir(clap::ArgMatches::default(), cwd); + #[cfg(feature = "starship")] + { + std::env::set_var("STARSHIP_SHELL", ""); + std::env::set_var("PWD", &cwd); + let mut starship_context = + starship::context::Context::new_with_dir(clap::ArgMatches::default(), cwd); - match starship_context.config.config { - None => { - starship_context.config.config = create_default_starship_config(); - } - Some(toml::Value::Table(t)) if t.is_empty() => { - starship_context.config.config = create_default_starship_config(); - } - _ => {} - }; - starship::print::get_prompt(starship_context) + match starship_context.config.config { + None => { + starship_context.config.config = create_default_starship_config(); + } + Some(toml::Value::Table(t)) if t.is_empty() => { + starship_context.config.config = create_default_starship_config(); + } + _ => {} + }; + starship::print::get_prompt(starship_context) + } + #[cfg(not(feature = "starship"))] + { + format!( + "\x1b[32m{}{}\x1b[m> ", + cwd, + match current_branch() { + Some(s) => format!("({})", s), + None => "".to_string(), + } + ) + } } else if let Some(prompt) = config.get("prompt") { let prompt_line = prompt.as_string()?; @@ -829,6 +847,7 @@ fn chomp_newline(s: &str) -> &str { } } +#[derive(Debug)] pub enum LineResult { Success(String), Error(String, ShellError), @@ -836,6 +855,36 @@ pub enum LineResult { Break, } +pub async fn parse_and_eval(line: &str, ctx: &mut Context) -> Result { + let line = if line.ends_with('\n') { + &line[..line.len() - 1] + } else { + line + }; + + let lite_result = nu_parser::lite_parse(&line, 0)?; + + // TODO ensure the command whose examples we're testing is actually in the pipeline + let mut classified_block = nu_parser::classify_block(&lite_result, ctx.registry()); + classified_block.block.expand_it_usage(); + + let input_stream = InputStream::empty(); + let env = ctx.get_env(); + + run_block( + &classified_block.block, + ctx, + input_stream, + &Value::nothing(), + &IndexMap::new(), + &env, + ) + .await? + .collect_string(Tag::unknown()) + .await + .map(|x| x.item) +} + /// Process the line by parsing the text to turn it into commands, classify those commands so that we understand what is being called in the pipeline, and then run this pipeline pub async fn process_line( readline: Result, @@ -912,7 +961,7 @@ pub async fn process_line( .unwrap_or(true) && canonicalize(ctx.shell_manager.path(), name).is_ok() && Path::new(&name).is_dir() - && which::which(&name).is_err() + && !crate::commands::classified::external::did_find_command(&name) { // Here we work differently if we're in Windows because of the expected Windows behavior #[cfg(windows)] diff --git a/crates/nu-cli/src/commands.rs b/crates/nu-cli/src/commands.rs index 45e06f115e..52b4629228 100644 --- a/crates/nu-cli/src/commands.rs +++ b/crates/nu-cli/src/commands.rs @@ -40,14 +40,12 @@ pub(crate) mod exit; pub(crate) mod first; pub(crate) mod format; pub(crate) mod from; -pub(crate) mod from_bson; pub(crate) mod from_csv; pub(crate) mod from_eml; pub(crate) mod from_ics; pub(crate) mod from_ini; pub(crate) mod from_json; pub(crate) mod from_ods; -pub(crate) mod from_sqlite; pub(crate) mod from_ssv; pub(crate) mod from_toml; pub(crate) mod from_tsv; @@ -104,12 +102,10 @@ pub(crate) mod str_; pub(crate) mod table; pub(crate) mod tags; pub(crate) mod to; -pub(crate) mod to_bson; pub(crate) mod to_csv; pub(crate) mod to_html; pub(crate) mod to_json; pub(crate) mod to_md; -pub(crate) mod to_sqlite; pub(crate) mod to_toml; pub(crate) mod to_tsv; pub(crate) mod to_url; @@ -168,15 +164,12 @@ pub(crate) use exit::Exit; pub(crate) use first::First; pub(crate) use format::Format; pub(crate) use from::From; -pub(crate) use from_bson::FromBSON; pub(crate) use from_csv::FromCSV; pub(crate) use from_eml::FromEML; pub(crate) use from_ics::FromIcs; pub(crate) use from_ini::FromINI; pub(crate) use from_json::FromJSON; pub(crate) use from_ods::FromODS; -pub(crate) use from_sqlite::FromDB; -pub(crate) use from_sqlite::FromSQLite; pub(crate) use from_ssv::FromSSV; pub(crate) use from_toml::FromTOML; pub(crate) use from_tsv::FromTSV; @@ -213,7 +206,9 @@ pub(crate) use pivot::Pivot; pub(crate) use prepend::Prepend; pub(crate) use prev::Previous; pub(crate) use pwd::Pwd; -pub(crate) use random::{Random, RandomBool, RandomDice, RandomUUID}; +#[cfg(feature = "uuid_crate")] +pub(crate) use random::RandomUUID; +pub(crate) use random::{Random, RandomBool, RandomDice}; pub(crate) use range::Range; pub(crate) use reject::Reject; pub(crate) use rename::Rename; @@ -236,13 +231,10 @@ pub(crate) use str_::{ pub(crate) use table::Table; pub(crate) use tags::Tags; pub(crate) use to::To; -pub(crate) use to_bson::ToBSON; pub(crate) use to_csv::ToCSV; pub(crate) use to_html::ToHTML; pub(crate) use to_json::ToJSON; pub(crate) use to_md::ToMarkdown; -pub(crate) use to_sqlite::ToDB; -pub(crate) use to_sqlite::ToSQLite; pub(crate) use to_toml::ToTOML; pub(crate) use to_tsv::ToTSV; pub(crate) use to_url::ToURL; diff --git a/crates/nu-cli/src/commands/classified/external.rs b/crates/nu-cli/src/commands/classified/external.rs index a9953d81df..8aae1165d8 100644 --- a/crates/nu-cli/src/commands/classified/external.rs +++ b/crates/nu-cli/src/commands/classified/external.rs @@ -26,7 +26,7 @@ pub(crate) async fn run_external_command( ) -> Result { trace!(target: "nu::run::external", "-> {}", command.name); - if !did_find_command(&command.name).await { + if !did_find_command(&command.name) { return Err(ShellError::labeled_error( "Command not found", "command not found", @@ -70,7 +70,18 @@ async fn run_with_stdin( let process_args = command_args .iter() .map(|arg| { - let arg = expand_tilde(arg.deref(), dirs::home_dir); + let home_dir; + + #[cfg(feature = "dirs")] + { + home_dir = dirs::home_dir; + } + #[cfg(not(feature = "dirs"))] + { + home_dir = || Some(std::path::PathBuf::from("/")); + } + + let arg = expand_tilde(arg.deref(), home_dir); #[cfg(not(windows))] { @@ -407,13 +418,19 @@ fn spawn( } } -async fn did_find_command(name: &str) -> bool { - #[cfg(not(windows))] +pub fn did_find_command(#[allow(unused)] name: &str) -> bool { + #[cfg(not(feature = "which"))] + { + // we can't perform this check, so just assume it can be found + true + } + + #[cfg(all(feature = "which", unix))] { which::which(name).is_ok() } - #[cfg(windows)] + #[cfg(all(feature = "which", windows))] { if which::which(name).is_ok() { true @@ -483,11 +500,17 @@ fn shell_os_paths() -> Vec { mod tests { use super::{ add_quotes, argument_contains_whitespace, argument_is_quoted, expand_tilde, remove_quotes, - run_external_command, Context, InputStream, }; + #[cfg(feature = "which")] + use super::{run_external_command, Context, InputStream}; + + #[cfg(feature = "which")] use futures::executor::block_on; + #[cfg(feature = "which")] use nu_errors::ShellError; + #[cfg(feature = "which")] use nu_protocol::Scope; + #[cfg(feature = "which")] use nu_test_support::commands::ExternalBuilder; // async fn read(mut stream: OutputStream) -> Option { @@ -503,6 +526,7 @@ mod tests { // } // } + #[cfg(feature = "which")] async fn non_existent_run() -> Result<(), ShellError> { let cmd = ExternalBuilder::for_name("i_dont_exist.exe").build(); @@ -542,6 +566,7 @@ mod tests { // block_on(failure_run()) // } + #[cfg(feature = "which")] #[test] fn identifies_command_not_found() -> Result<(), ShellError> { block_on(non_existent_run()) diff --git a/crates/nu-cli/src/commands/random/mod.rs b/crates/nu-cli/src/commands/random/mod.rs index 3f1b9e8974..0dbacfd80d 100644 --- a/crates/nu-cli/src/commands/random/mod.rs +++ b/crates/nu-cli/src/commands/random/mod.rs @@ -2,10 +2,12 @@ pub mod command; pub mod bool; pub mod dice; +#[cfg(feature = "uuid_crate")] pub mod uuid; pub use command::Command as Random; pub use self::bool::SubCommand as RandomBool; pub use dice::SubCommand as RandomDice; +#[cfg(feature = "uuid_crate")] pub use uuid::SubCommand as RandomUUID; diff --git a/crates/nu-cli/src/commands/run_external.rs b/crates/nu-cli/src/commands/run_external.rs index 135e9a1ebe..042242628c 100644 --- a/crates/nu-cli/src/commands/run_external.rs +++ b/crates/nu-cli/src/commands/run_external.rs @@ -157,7 +157,7 @@ async fn maybe_autocd_dir<'a>(cmd: &ExternalCommand, ctx: &mut Context) -> Optio || (cmd.args.is_empty() && PathBuf::from(name).is_dir() && dunce::canonicalize(name).is_ok() - && which::which(&name).is_err()) + && !crate::commands::classified::external::did_find_command(&name)) { Some(name) } else { diff --git a/crates/nu-cli/src/commands/which_.rs b/crates/nu-cli/src/commands/which_.rs index 2515cf8851..e9ee99ab0e 100644 --- a/crates/nu-cli/src/commands/which_.rs +++ b/crates/nu-cli/src/commands/which_.rs @@ -60,6 +60,7 @@ macro_rules! entry_builtin { }; } +#[allow(unused)] macro_rules! entry_path { ($arg:expr, $path:expr, $tag:expr) => { entry( @@ -99,13 +100,16 @@ async fn which(args: CommandArgs, registry: &CommandRegistry) -> Result Result> { + pub fn basic() -> Result> { let registry = CommandRegistry::new(); #[cfg(windows)] diff --git a/crates/nu-cli/src/data/config.rs b/crates/nu-cli/src/data/config.rs index 391b5db1bb..10169c1c80 100644 --- a/crates/nu-cli/src/data/config.rs +++ b/crates/nu-cli/src/data/config.rs @@ -10,7 +10,6 @@ pub(crate) use nuconfig::NuConfig; use crate::commands::from_toml::convert_toml_value_to_nu_value; use crate::commands::to_toml::value_to_toml_value; use crate::prelude::*; -use directories::ProjectDirs; use indexmap::IndexMap; use log::trace; use nu_errors::ShellError; @@ -20,8 +19,25 @@ use std::fs::{self, OpenOptions}; use std::io; use std::path::{Path, PathBuf}; +#[cfg(feature = "directories")] pub fn config_path() -> Result { - app_path("config", ProjectDirs::config_dir) + use directories::ProjectDirs; + + let dir = ProjectDirs::from("org", "nushell", "nu") + .ok_or_else(|| ShellError::untagged_runtime_error("Couldn't find project directory"))?; + let path = ProjectDirs::config_dir(&dir).to_owned(); + std::fs::create_dir_all(&path).map_err(|err| { + ShellError::untagged_runtime_error(&format!("Couldn't create {} path:\n{}", "config", err)) + })?; + + Ok(path) +} + +#[cfg(not(feature = "directories"))] +pub fn config_path() -> Result { + // FIXME: unsure if this should be error or a simple default + + Ok(std::path::PathBuf::from("/")) } pub fn default_path() -> Result { @@ -39,21 +55,30 @@ pub fn default_path_for(file: &Option) -> Result { Ok(filename) } +#[cfg(feature = "directories")] pub fn user_data() -> Result { - app_path("user data", ProjectDirs::data_local_dir) -} + use directories::ProjectDirs; -fn app_path &Path>(display: &str, f: F) -> Result { let dir = ProjectDirs::from("org", "nushell", "nu") .ok_or_else(|| ShellError::untagged_runtime_error("Couldn't find project directory"))?; - let path = f(&dir).to_owned(); + let path = ProjectDirs::data_local_dir(&dir).to_owned(); std::fs::create_dir_all(&path).map_err(|err| { - ShellError::untagged_runtime_error(&format!("Couldn't create {} path:\n{}", display, err)) + ShellError::untagged_runtime_error(&format!( + "Couldn't create {} path:\n{}", + "user data", err + )) })?; Ok(path) } +#[cfg(not(feature = "directories"))] +pub fn user_data() -> Result { + // FIXME: unsure if this should be error or a simple default + + Ok(std::path::PathBuf::from("/")) +} + pub fn read( tag: impl Into, at: &Option, diff --git a/crates/nu-cli/src/env/host.rs b/crates/nu-cli/src/env/host.rs index 4c490a8343..6abb628c8a 100644 --- a/crates/nu-cli/src/env/host.rs +++ b/crates/nu-cli/src/env/host.rs @@ -6,9 +6,6 @@ use std::ffi::OsString; use std::fmt::Debug; pub trait Host: Debug + Send { - fn out_terminal(&self) -> Option>; - fn err_terminal(&self) -> Option>; - fn out_termcolor(&self) -> termcolor::StandardStream; fn err_termcolor(&self) -> termcolor::StandardStream; @@ -24,14 +21,6 @@ pub trait Host: Debug + Send { } impl Host for Box { - fn out_terminal(&self) -> Option> { - (**self).out_terminal() - } - - fn err_terminal(&self) -> Option> { - (**self).err_terminal() - } - fn stdout(&mut self, out: &str) { (**self).stdout(out) } @@ -73,14 +62,6 @@ impl Host for Box { pub struct BasicHost; impl Host for BasicHost { - fn out_terminal(&self) -> Option> { - term::stdout() - } - - fn err_terminal(&self) -> Option> { - term::stderr() - } - fn stdout(&mut self, out: &str) { match out { "\n" => outln!(""), @@ -96,19 +77,40 @@ impl Host for BasicHost { } fn vars(&mut self) -> Vec<(String, String)> { - std::env::vars().collect::>() + #[cfg(not(target_arch = "wasm32"))] + { + std::env::vars().collect::>() + } + + #[cfg(target_arch = "wasm32")] + { + vec![] + } } fn env_get(&mut self, key: OsString) -> Option { - std::env::var_os(key) + #[cfg(not(target_arch = "wasm32"))] + { + std::env::var_os(key) + } + #[cfg(target_arch = "wasm32")] + { + None + } } fn env_set(&mut self, key: OsString, value: OsString) { - std::env::set_var(key, value); + #[cfg(not(target_arch = "wasm32"))] + { + std::env::set_var(key, value); + } } fn env_rm(&mut self, key: OsString) { - std::env::remove_var(key); + #[cfg(not(target_arch = "wasm32"))] + { + std::env::remove_var(key); + } } fn out_termcolor(&self) -> termcolor::StandardStream { @@ -145,14 +147,6 @@ impl FakeHost { #[cfg(test)] impl Host for FakeHost { - fn out_terminal(&self) -> Option> { - None - } - - fn err_terminal(&self) -> Option> { - None - } - fn stdout(&mut self, out: &str) { self.line_written = out.to_string(); } diff --git a/crates/nu-cli/src/evaluate/variables.rs b/crates/nu-cli/src/evaluate/variables.rs index 2eabb5de3d..33c7f51a12 100644 --- a/crates/nu-cli/src/evaluate/variables.rs +++ b/crates/nu-cli/src/evaluate/variables.rs @@ -32,7 +32,7 @@ pub fn nu(env: &IndexMap, tag: impl Into) -> Result Option { if let Ok(config) = crate::data::config::config(Tag::unknown()) { @@ -13,19 +11,29 @@ pub fn current_branch() -> Option { }; if !use_starship { - let v: Vec = vec![]; - match Repository::open_ext(".", RepositoryOpenFlags::empty(), v) { - Ok(repo) => { - let r = repo.head(); - match r { - Ok(r) => match r.shorthand() { - Some(s) => Some(s.to_string()), - None => None, - }, - _ => None, + #[cfg(feature = "git2")] + { + use git2::{Repository, RepositoryOpenFlags}; + use std::ffi::OsString; + + let v: Vec = vec![]; + match Repository::open_ext(".", RepositoryOpenFlags::empty(), v) { + Ok(repo) => { + let r = repo.head(); + match r { + Ok(r) => match r.shorthand() { + Some(s) => Some(s.to_string()), + None => None, + }, + _ => None, + } } + _ => None, } - _ => None, + } + #[cfg(not(feature = "git2"))] + { + None } } else { None diff --git a/crates/nu-cli/src/lib.rs b/crates/nu-cli/src/lib.rs index 756921969b..7b32571033 100644 --- a/crates/nu-cli/src/lib.rs +++ b/crates/nu-cli/src/lib.rs @@ -34,14 +34,14 @@ pub mod utils; mod examples; pub use crate::cli::{ - cli, create_default_context, load_plugins, process_line, run_pipeline_standalone, - run_vec_of_pipelines, LineResult, + cli, create_default_context, load_plugins, parse_and_eval, process_line, + run_pipeline_standalone, run_vec_of_pipelines, LineResult, }; pub use crate::commands::command::{ whole_stream_command, CommandArgs, EvaluatedWholeStreamCommandArgs, WholeStreamCommand, }; pub use crate::commands::help::get_help; -pub use crate::context::CommandRegistry; +pub use crate::context::{CommandRegistry, Context}; pub use crate::data::config; pub use crate::data::dict::TaggedListBuilder; pub use crate::data::primitive; diff --git a/crates/nu-cli/src/shell/completer.rs b/crates/nu-cli/src/shell/completer.rs index 1dad53abd9..20dccfb412 100644 --- a/crates/nu-cli/src/shell/completer.rs +++ b/crates/nu-cli/src/shell/completer.rs @@ -3,8 +3,9 @@ use crate::context::CommandRegistry; use crate::data::config; use crate::prelude::*; use derive_new::new; -#[cfg(windows)] +#[cfg(all(windows, feature = "ichwh"))] use ichwh::IchwhError; +#[cfg(all(windows, feature = "ichwh"))] use ichwh::IchwhResult; use indexmap::set::IndexSet; use rustyline::completion::{Completer, FilenameCompleter}; @@ -225,35 +226,49 @@ impl NuCompleter { } #[cfg(windows)] - fn is_executable(&self, file: &DirEntry) -> IchwhResult { - let file_type = file.metadata()?.file_type(); + fn is_executable(&self, file: &DirEntry) -> bool { + if let Ok(metadata) = file.metadata() { + let file_type = metadata.file_type(); - // If the entry isn't a file, it cannot be executable - if !(file_type.is_file() || file_type.is_symlink()) { - return Ok(false); - } + // If the entry isn't a file, it cannot be executable + if !(file_type.is_file() || file_type.is_symlink()) { + return false; + } - if let Some(extension) = file.path().extension() { - let exts = self.pathext()?; - - Ok(exts - .iter() - .any(|ext| extension.to_string_lossy().eq_ignore_ascii_case(ext))) + if let Some(extension) = file.path().extension() { + if let Ok(exts) = self.pathext() { + exts.iter() + .any(|ext| extension.to_string_lossy().eq_ignore_ascii_case(ext)) + } else { + false + } + } else { + false + } } else { - Ok(false) + false } } + #[cfg(target_arch = "wasm32")] + fn is_executable(&self, file: &DirEntry) -> bool { + false + } + #[cfg(unix)] - fn is_executable(&self, file: &DirEntry) -> IchwhResult { - let metadata = file.metadata()?; + fn is_executable(&self, file: &DirEntry) -> bool { + let metadata = file.metadata(); - let filetype = metadata.file_type(); - let permissions = metadata.permissions(); + if let Ok(metadata) = metadata { + let filetype = metadata.file_type(); + let permissions = metadata.permissions(); - // The file is executable if it is a directory or a symlink and the permissions are set for - // owner, group, or other - Ok((filetype.is_file() || filetype.is_symlink()) && (permissions.mode() & 0o111 != 0)) + // The file is executable if it is a directory or a symlink and the permissions are set for + // owner, group, or other + (filetype.is_file() || filetype.is_symlink()) && (permissions.mode() & 0o111 != 0) + } else { + false + } } fn find_path_executables(&self) -> Option> { @@ -264,7 +279,7 @@ impl NuCompleter { for path in paths { if let Ok(mut contents) = read_dir(path) { while let Some(Ok(item)) = contents.next() { - if let Ok(true) = self.is_executable(&item) { + if self.is_executable(&item) { if let Ok(name) = item.file_name().into_string() { executables.insert(name); } diff --git a/crates/nu-cli/src/shell/filesystem_shell.rs b/crates/nu-cli/src/shell/filesystem_shell.rs index cdfc627238..343da5db33 100644 --- a/crates/nu-cli/src/shell/filesystem_shell.rs +++ b/crates/nu-cli/src/shell/filesystem_shell.rs @@ -61,7 +61,10 @@ impl Clone for FilesystemShell { impl FilesystemShell { pub fn basic(commands: CommandRegistry) -> Result { - let path = std::env::current_dir()?; + let path = match std::env::current_dir() { + Ok(path) => path, + Err(_) => PathBuf::from("/"), + }; Ok(FilesystemShell { path: path.to_string_lossy().to_string(), @@ -69,7 +72,7 @@ impl FilesystemShell { completer: NuCompleter { file_completer: FilenameCompleter::new(), commands, - homedir: dirs::home_dir(), + homedir: homedir_if_possible(), }, hinter: HistoryHinter {}, }) @@ -89,20 +92,32 @@ impl FilesystemShell { completer: NuCompleter { file_completer: FilenameCompleter::new(), commands, - homedir: dirs::home_dir(), + homedir: homedir_if_possible(), }, hinter: HistoryHinter {}, }) } } +pub fn homedir_if_possible() -> Option { + #[cfg(feature = "dirs")] + { + dirs::home_dir() + } + + #[cfg(not(feature = "dirs"))] + { + None + } +} + impl Shell for FilesystemShell { fn name(&self) -> String { "filesystem".to_string() } fn homedir(&self) -> Option { - dirs::home_dir() + homedir_if_possible() } fn ls( @@ -195,7 +210,7 @@ impl Shell for FilesystemShell { fn cd(&self, args: CdArgs, name: Tag) -> Result { let path = match args.path { - None => match dirs::home_dir() { + None => match homedir_if_possible() { Some(o) => o, _ => { return Err(ShellError::labeled_error( diff --git a/crates/nu-cli/src/shell/help_shell.rs b/crates/nu-cli/src/shell/help_shell.rs index cb7e2759f2..5795962517 100644 --- a/crates/nu-cli/src/shell/help_shell.rs +++ b/crates/nu-cli/src/shell/help_shell.rs @@ -126,7 +126,15 @@ impl Shell for HelpShell { } fn homedir(&self) -> Option { - dirs::home_dir() + #[cfg(feature = "dirs")] + { + dirs::home_dir() + } + + #[cfg(not(feature = "dirs"))] + { + None + } } fn path(&self) -> String { diff --git a/crates/nu-cli/src/stream/input.rs b/crates/nu-cli/src/stream/input.rs index 479ac81a93..280e0ff2d9 100644 --- a/crates/nu-cli/src/stream/input.rs +++ b/crates/nu-cli/src/stream/input.rs @@ -126,9 +126,8 @@ impl InputStream { } Some(Value { tag: value_tag, - value: v, + value: _, }) => { - println!("{:?}", v); return Err(ShellError::labeled_error_with_secondary( "Expected binary from pipeline", "requires binary input", diff --git a/crates/nu-cli/tests/commands/cd.rs b/crates/nu-cli/tests/commands/cd.rs index 1d7455c9ae..1940001a08 100644 --- a/crates/nu-cli/tests/commands/cd.rs +++ b/crates/nu-cli/tests/commands/cd.rs @@ -127,6 +127,7 @@ fn filesystem_change_current_directory_to_parent_directory_after_delete_cwd() { }) } +#[cfg(feature = "dirs")] #[test] fn filesystem_change_to_home_directory() { Playground::setup("cd_test_8", |dirs, _| { diff --git a/crates/nu-cli/tests/commands/open.rs b/crates/nu-cli/tests/commands/open.rs index 22a91173c2..c365f4df20 100644 --- a/crates/nu-cli/tests/commands/open.rs +++ b/crates/nu-cli/tests/commands/open.rs @@ -54,6 +54,7 @@ fn parses_csv() { // what appears to be an issue in the bson library that is under investigation. // +#[cfg(feature = "bson")] #[test] fn parses_bson() { let actual = nu!( @@ -64,6 +65,7 @@ fn parses_bson() { assert_eq!(actual.out, "hello"); } +#[cfg(feature = "bson")] #[test] fn parses_more_bson_complexity() { let actual = nu!( @@ -130,6 +132,7 @@ fn parses_more_bson_complexity() { // 4 │ // ━━━┷━━━━━━ +#[cfg(feature = "sqlite")] #[test] fn parses_sqlite() { let actual = nu!( diff --git a/crates/nu-cli/tests/commands/random/mod.rs b/crates/nu-cli/tests/commands/random/mod.rs index 52dbdc2f8f..4d82049428 100644 --- a/crates/nu-cli/tests/commands/random/mod.rs +++ b/crates/nu-cli/tests/commands/random/mod.rs @@ -1,3 +1,4 @@ mod bool; mod dice; +#[cfg(feature = "uuid_crate")] mod uuid; diff --git a/crates/nu-cli/tests/commands/update.rs b/crates/nu-cli/tests/commands/update.rs index 7e270e9494..bd5a524ec0 100644 --- a/crates/nu-cli/tests/commands/update.rs +++ b/crates/nu-cli/tests/commands/update.rs @@ -15,6 +15,7 @@ fn sets_the_column() { assert_eq!(actual.out, "0.7.0"); } +#[cfg(features = "inc")] #[test] fn sets_the_column_from_a_block_run_output() { let actual = nu!( diff --git a/crates/nu-cli/tests/commands/where_.rs b/crates/nu-cli/tests/commands/where_.rs index 41a0a59453..b8979ee9bf 100644 --- a/crates/nu-cli/tests/commands/where_.rs +++ b/crates/nu-cli/tests/commands/where_.rs @@ -1,4 +1,7 @@ -use nu_test_support::{nu, pipeline}; +use nu_test_support::nu; + +#[cfg(feature = "sqlite")] +use nu_test_support::pipeline; #[test] fn filters_by_unit_size_comparison() { @@ -40,6 +43,7 @@ fn where_not_in_table() { assert_eq!(actual.out, "4"); } +#[cfg(feature = "sqlite")] #[test] fn explicit_block_condition() { let actual = nu!( @@ -58,6 +62,7 @@ fn explicit_block_condition() { assert_eq!(actual.out, "4253"); } +#[cfg(feature = "sqlite")] #[test] fn binary_operator_comparisons() { let actual = nu!( @@ -136,6 +141,7 @@ fn binary_operator_comparisons() { assert_eq!(actual.out, "42"); } +#[cfg(feature = "sqlite")] #[test] fn contains_operator() { let actual = nu!( diff --git a/crates/nu-cli/tests/format_conversions/bson.rs b/crates/nu-cli/tests/format_conversions/bson.rs index 72133b0025..d5d408effe 100644 --- a/crates/nu-cli/tests/format_conversions/bson.rs +++ b/crates/nu-cli/tests/format_conversions/bson.rs @@ -1,7 +1,7 @@ -use nu_test_support::{nu, pipeline}; - +#[cfg(feature = "bson")] #[test] fn table_to_bson_and_back_into_table() { + use nu_test_support::{nu, pipeline}; let actual = nu!( cwd: "tests/fixtures/formats", pipeline( r#" diff --git a/crates/nu-cli/tests/format_conversions/sqlite.rs b/crates/nu-cli/tests/format_conversions/sqlite.rs index 9f42ce1f21..7f6249ad0f 100644 --- a/crates/nu-cli/tests/format_conversions/sqlite.rs +++ b/crates/nu-cli/tests/format_conversions/sqlite.rs @@ -1,5 +1,7 @@ +#[cfg(feature = "sqlite")] use nu_test_support::{nu, pipeline}; +#[cfg(feature = "sqlite")] #[test] fn table_to_sqlite_and_back_into_table() { let actual = nu!( diff --git a/crates/nu-test-support/src/playground.rs b/crates/nu-test-support/src/playground.rs index 4ebeef0c0d..a86320de59 100644 --- a/crates/nu-test-support/src/playground.rs +++ b/crates/nu-test-support/src/playground.rs @@ -95,6 +95,7 @@ impl Playground { self } + #[cfg(not(target_arch = "wasm32"))] pub fn symlink(&mut self, from: impl AsRef, to: impl AsRef) -> &mut Self { let from = self.cwd.join(from); let to = self.cwd.join(to); diff --git a/crates/nu_plugin_from_bson/Cargo.toml b/crates/nu_plugin_from_bson/Cargo.toml new file mode 100644 index 0000000000..17a52d150d --- /dev/null +++ b/crates/nu_plugin_from_bson/Cargo.toml @@ -0,0 +1,23 @@ +[package] +authors = ["The Nu Project Contributors"] +description = "A converter plugin to the bson format for Nushell" +edition = "2018" +license = "MIT" +name = "nu_plugin_from_bson" +version = "0.16.1" + +[lib] +doctest = false + +[dependencies] +bigdecimal = "0.1.2" +bson = {version = "0.14.1", features = ["decimal128"]} +nu-errors = {path = "../nu-errors", version = "0.16.1"} +nu-plugin = {path = "../nu-plugin", version = "0.16.1"} +nu-protocol = {path = "../nu-protocol", version = "0.16.1"} +nu-source = {path = "../nu-source", version = "0.16.1"} +nu-value-ext = {path = "../nu-value-ext", version = "0.16.1"} +num-traits = "0.2.12" + +[build-dependencies] +nu-build = {version = "0.16.1", path = "../nu-build"} diff --git a/crates/nu_plugin_from_bson/build.rs b/crates/nu_plugin_from_bson/build.rs new file mode 100644 index 0000000000..b7511cfc6a --- /dev/null +++ b/crates/nu_plugin_from_bson/build.rs @@ -0,0 +1,3 @@ +fn main() -> Result<(), Box> { + nu_build::build() +} diff --git a/crates/nu-cli/src/commands/from_bson.rs b/crates/nu_plugin_from_bson/src/from_bson.rs similarity index 79% rename from crates/nu-cli/src/commands/from_bson.rs rename to crates/nu_plugin_from_bson/src/from_bson.rs index 8c7b2d3cef..978e4c5402 100644 --- a/crates/nu-cli/src/commands/from_bson.rs +++ b/crates/nu_plugin_from_bson/src/from_bson.rs @@ -1,41 +1,22 @@ -use crate::commands::WholeStreamCommand; -use crate::prelude::*; +use bigdecimal::BigDecimal; use bson::{decode_document, spec::BinarySubtype, Bson}; use nu_errors::{ExpectedRange, ShellError}; -use nu_protocol::{Primitive, ReturnSuccess, Signature, TaggedDictBuilder, UntaggedValue, Value}; -use nu_source::SpannedItem; +use nu_protocol::{Primitive, ReturnSuccess, ReturnValue, TaggedDictBuilder, UntaggedValue, Value}; +use nu_source::{SpannedItem, Tag}; use std::str::FromStr; -pub struct FromBSON; +#[derive(Default)] +pub struct FromBSON { + pub state: Vec, + pub name_tag: Tag, +} -#[async_trait] -impl WholeStreamCommand for FromBSON { - fn name(&self) -> &str { - "from bson" - } - - fn signature(&self) -> Signature { - Signature::build("from bson") - } - - fn usage(&self) -> &str { - "Parse binary as .bson and create table." - } - - async fn run( - &self, - args: CommandArgs, - registry: &CommandRegistry, - ) -> Result { - from_bson(args, registry).await - } - - fn examples(&self) -> Vec { - vec![Example { - description: "Convert bson data to a table", - example: "open file.bin | from bson", - result: None, - }] +impl FromBSON { + pub fn new() -> FromBSON { + FromBSON { + state: vec![], + name_tag: Tag::unknown(), + } } } @@ -208,37 +189,13 @@ pub fn from_bson_bytes_to_value(bytes: Vec, tag: impl Into) -> Result Result { - let registry = registry.clone(); - let args = args.evaluate_once(®istry).await?; - let tag = args.name_tag(); - let input = args.input; - - let bytes = input.collect_binary(tag.clone()).await?; - - match from_bson_bytes_to_value(bytes.item, tag.clone()) { - Ok(x) => Ok(OutputStream::one(ReturnSuccess::value(x))), - Err(_) => Err(ShellError::labeled_error_with_secondary( +pub fn from_bson(bytes: Vec, name_tag: Tag) -> Result, ShellError> { + match from_bson_bytes_to_value(bytes, name_tag.clone()) { + Ok(x) => Ok(vec![ReturnSuccess::value(x)]), + Err(_) => Err(ShellError::labeled_error( "Could not parse as BSON", "input cannot be parsed as BSON", - tag.clone(), - "value originates from here", - bytes.tag, + name_tag, )), } } - -#[cfg(test)] -mod tests { - use super::FromBSON; - - #[test] - fn examples_work_as_expected() { - use crate::examples::test as test_examples; - - test_examples(FromBSON {}) - } -} diff --git a/crates/nu_plugin_from_bson/src/lib.rs b/crates/nu_plugin_from_bson/src/lib.rs new file mode 100644 index 0000000000..2670658005 --- /dev/null +++ b/crates/nu_plugin_from_bson/src/lib.rs @@ -0,0 +1,4 @@ +mod from_bson; +mod nu; + +pub use from_bson::FromBSON; diff --git a/crates/nu_plugin_from_bson/src/main.rs b/crates/nu_plugin_from_bson/src/main.rs new file mode 100644 index 0000000000..c3db0e7341 --- /dev/null +++ b/crates/nu_plugin_from_bson/src/main.rs @@ -0,0 +1,6 @@ +use nu_plugin::serve_plugin; +use nu_plugin_from_bson::FromBSON; + +fn main() { + serve_plugin(&mut FromBSON::new()) +} diff --git a/crates/nu_plugin_from_bson/src/nu/mod.rs b/crates/nu_plugin_from_bson/src/nu/mod.rs new file mode 100644 index 0000000000..ca0a26112b --- /dev/null +++ b/crates/nu_plugin_from_bson/src/nu/mod.rs @@ -0,0 +1,46 @@ +#[cfg(test)] +mod tests; + +use crate::FromBSON; +use nu_errors::ShellError; +use nu_plugin::Plugin; +use nu_protocol::{CallInfo, Primitive, ReturnValue, Signature, UntaggedValue, Value}; +use nu_source::Tag; + +impl Plugin for FromBSON { + fn config(&mut self) -> Result { + Ok(Signature::build("from bson") + .desc("Convert from .bson binary into table") + .filter()) + } + + fn begin_filter(&mut self, call_info: CallInfo) -> Result, ShellError> { + self.name_tag = call_info.name_tag; + Ok(vec![]) + } + + fn filter(&mut self, input: Value) -> Result, ShellError> { + match input { + Value { + value: UntaggedValue::Primitive(Primitive::Binary(b)), + .. + } => { + self.state.extend_from_slice(&b); + } + Value { tag, .. } => { + return Err(ShellError::labeled_error_with_secondary( + "Expected binary from pipeline", + "requires binary input", + self.name_tag.clone(), + "value originates from here", + tag, + )); + } + } + Ok(vec![]) + } + + fn end_filter(&mut self) -> Result, ShellError> { + crate::from_bson::from_bson(self.state.clone(), Tag::unknown()) + } +} diff --git a/crates/nu_plugin_from_bson/src/nu/tests.rs b/crates/nu_plugin_from_bson/src/nu/tests.rs new file mode 100644 index 0000000000..d14c19bee3 --- /dev/null +++ b/crates/nu_plugin_from_bson/src/nu/tests.rs @@ -0,0 +1 @@ +mod integration {} diff --git a/crates/nu_plugin_from_sqlite/Cargo.toml b/crates/nu_plugin_from_sqlite/Cargo.toml new file mode 100644 index 0000000000..657ff6d185 --- /dev/null +++ b/crates/nu_plugin_from_sqlite/Cargo.toml @@ -0,0 +1,27 @@ +[package] +authors = ["The Nu Project Contributors"] +description = "A converter plugin to the bson format for Nushell" +edition = "2018" +license = "MIT" +name = "nu_plugin_from_sqlite" +version = "0.16.1" + +[lib] +doctest = false + +[dependencies] +bigdecimal = "0.1.2" +nu-errors = {path = "../nu-errors", version = "0.16.1"} +nu-plugin = {path = "../nu-plugin", version = "0.16.1"} +nu-protocol = {path = "../nu-protocol", version = "0.16.1"} +nu-source = {path = "../nu-source", version = "0.16.1"} +nu-value-ext = {path = "../nu-value-ext", version = "0.16.1"} +num-traits = "0.2.12" +tempfile = "3.1.0" + +[dependencies.rusqlite] +features = ["bundled", "blob"] +version = "0.23.1" + +[build-dependencies] +nu-build = {version = "0.16.1", path = "../nu-build"} diff --git a/crates/nu_plugin_from_sqlite/build.rs b/crates/nu_plugin_from_sqlite/build.rs new file mode 100644 index 0000000000..b7511cfc6a --- /dev/null +++ b/crates/nu_plugin_from_sqlite/build.rs @@ -0,0 +1,3 @@ +fn main() -> Result<(), Box> { + nu_build::build() +} diff --git a/crates/nu-cli/src/commands/from_sqlite.rs b/crates/nu_plugin_from_sqlite/src/from_sqlite.rs similarity index 60% rename from crates/nu-cli/src/commands/from_sqlite.rs rename to crates/nu_plugin_from_sqlite/src/from_sqlite.rs index c74fabd392..238ca9b4f8 100644 --- a/crates/nu-cli/src/commands/from_sqlite.rs +++ b/crates/nu_plugin_from_sqlite/src/from_sqlite.rs @@ -1,58 +1,22 @@ -use crate::commands::WholeStreamCommand; -use crate::prelude::*; use nu_errors::ShellError; -use nu_protocol::{Primitive, Signature, TaggedDictBuilder, UntaggedValue, Value}; +use nu_protocol::{Primitive, ReturnSuccess, ReturnValue, TaggedDictBuilder, UntaggedValue, Value}; +use nu_source::Tag; use rusqlite::{types::ValueRef, Connection, Row, NO_PARAMS}; use std::io::Write; use std::path::Path; -pub struct FromSQLite; - -#[async_trait] -impl WholeStreamCommand for FromSQLite { - fn name(&self) -> &str { - "from sqlite" - } - - fn signature(&self) -> Signature { - Signature::build("from sqlite") - } - - fn usage(&self) -> &str { - "Parse binary data as sqlite .db and create table." - } - - async fn run( - &self, - args: CommandArgs, - registry: &CommandRegistry, - ) -> Result { - from_sqlite(args, registry).await - } +#[derive(Default)] +pub struct FromSqlite { + pub state: Vec, + pub name_tag: Tag, } -pub struct FromDB; - -#[async_trait] -impl WholeStreamCommand for FromDB { - fn name(&self) -> &str { - "from db" - } - - fn signature(&self) -> Signature { - Signature::build("from db") - } - - fn usage(&self) -> &str { - "Parse binary data as db and create table." - } - - async fn run( - &self, - args: CommandArgs, - registry: &CommandRegistry, - ) -> Result { - from_sqlite(args, registry).await +impl FromSqlite { + pub fn new() -> FromSqlite { + FromSqlite { + state: vec![], + name_tag: Tag::unknown(), + } } } @@ -135,47 +99,19 @@ pub fn from_sqlite_bytes_to_value( } } -async fn from_sqlite( - args: CommandArgs, - registry: &CommandRegistry, -) -> Result { - let registry = registry.clone(); - let args = args.evaluate_once(®istry).await?; - let tag = args.name_tag(); - let input = args.input; - - let bytes = input.collect_binary(tag.clone()).await?; - - match from_sqlite_bytes_to_value(bytes.item, tag.clone()) { +pub fn from_sqlite(bytes: Vec, name_tag: Tag) -> Result, ShellError> { + match from_sqlite_bytes_to_value(bytes, name_tag.clone()) { Ok(x) => match x { Value { value: UntaggedValue::Table(list), .. - } => Ok(futures::stream::iter(list).to_output_stream()), - _ => Ok(OutputStream::one(x)), + } => Ok(list.into_iter().map(ReturnSuccess::value).collect()), + _ => Ok(vec![ReturnSuccess::value(x)]), }, - Err(err) => { - println!("{:?}", err); - - Err(ShellError::labeled_error_with_secondary( - "Could not parse as SQLite", - "input cannot be parsed as SQLite", - &tag, - "value originates from here", - bytes.tag, - )) - } - } -} - -#[cfg(test)] -mod tests { - use super::FromSQLite; - - #[test] - fn examples_work_as_expected() { - use crate::examples::test as test_examples; - - test_examples(FromSQLite {}) + Err(_) => Err(ShellError::labeled_error( + "Could not parse as SQLite", + "input cannot be parsed as SQLite", + &name_tag, + )), } } diff --git a/crates/nu_plugin_from_sqlite/src/lib.rs b/crates/nu_plugin_from_sqlite/src/lib.rs new file mode 100644 index 0000000000..62e49fb460 --- /dev/null +++ b/crates/nu_plugin_from_sqlite/src/lib.rs @@ -0,0 +1,4 @@ +mod from_sqlite; +mod nu; + +pub use from_sqlite::FromSqlite; diff --git a/crates/nu_plugin_from_sqlite/src/main.rs b/crates/nu_plugin_from_sqlite/src/main.rs new file mode 100644 index 0000000000..26147638a5 --- /dev/null +++ b/crates/nu_plugin_from_sqlite/src/main.rs @@ -0,0 +1,6 @@ +use nu_plugin::serve_plugin; +use nu_plugin_from_sqlite::FromSqlite; + +fn main() { + serve_plugin(&mut FromSqlite::new()) +} diff --git a/crates/nu_plugin_from_sqlite/src/nu/mod.rs b/crates/nu_plugin_from_sqlite/src/nu/mod.rs new file mode 100644 index 0000000000..deac5dafcd --- /dev/null +++ b/crates/nu_plugin_from_sqlite/src/nu/mod.rs @@ -0,0 +1,46 @@ +#[cfg(test)] +mod tests; + +use crate::FromSqlite; +use nu_errors::ShellError; +use nu_plugin::Plugin; +use nu_protocol::{CallInfo, Primitive, ReturnValue, Signature, UntaggedValue, Value}; +use nu_source::Tag; + +impl Plugin for FromSqlite { + fn config(&mut self) -> Result { + Ok(Signature::build("from sqlite") + .desc("Convert from sqlite binary into table") + .filter()) + } + + fn begin_filter(&mut self, call_info: CallInfo) -> Result, ShellError> { + self.name_tag = call_info.name_tag; + Ok(vec![]) + } + + fn filter(&mut self, input: Value) -> Result, ShellError> { + match input { + Value { + value: UntaggedValue::Primitive(Primitive::Binary(b)), + .. + } => { + self.state.extend_from_slice(&b); + } + Value { tag, .. } => { + return Err(ShellError::labeled_error_with_secondary( + "Expected binary from pipeline", + "requires binary input", + self.name_tag.clone(), + "value originates from here", + tag, + )); + } + } + Ok(vec![]) + } + + fn end_filter(&mut self) -> Result, ShellError> { + crate::from_sqlite::from_sqlite(self.state.clone(), Tag::unknown()) + } +} diff --git a/crates/nu_plugin_from_sqlite/src/nu/tests.rs b/crates/nu_plugin_from_sqlite/src/nu/tests.rs new file mode 100644 index 0000000000..d14c19bee3 --- /dev/null +++ b/crates/nu_plugin_from_sqlite/src/nu/tests.rs @@ -0,0 +1 @@ +mod integration {} diff --git a/crates/nu_plugin_to_bson/Cargo.toml b/crates/nu_plugin_to_bson/Cargo.toml new file mode 100644 index 0000000000..a49db6496d --- /dev/null +++ b/crates/nu_plugin_to_bson/Cargo.toml @@ -0,0 +1,22 @@ +[package] +authors = ["The Nu Project Contributors"] +description = "A converter plugin to the bson format for Nushell" +edition = "2018" +license = "MIT" +name = "nu_plugin_to_bson" +version = "0.16.1" + +[lib] +doctest = false + +[dependencies] +bson = "0.14.1" +nu-errors = {path = "../nu-errors", version = "0.16.1"} +nu-plugin = {path = "../nu-plugin", version = "0.16.1"} +nu-protocol = {path = "../nu-protocol", version = "0.16.1"} +nu-source = {path = "../nu-source", version = "0.16.1"} +nu-value-ext = {path = "../nu-value-ext", version = "0.16.1"} +num-traits = "0.2.12" + +[build-dependencies] +nu-build = {version = "0.16.1", path = "../nu-build"} diff --git a/crates/nu_plugin_to_bson/build.rs b/crates/nu_plugin_to_bson/build.rs new file mode 100644 index 0000000000..b7511cfc6a --- /dev/null +++ b/crates/nu_plugin_to_bson/build.rs @@ -0,0 +1,3 @@ +fn main() -> Result<(), Box> { + nu_build::build() +} diff --git a/crates/nu_plugin_to_bson/src/lib.rs b/crates/nu_plugin_to_bson/src/lib.rs new file mode 100644 index 0000000000..b823777b51 --- /dev/null +++ b/crates/nu_plugin_to_bson/src/lib.rs @@ -0,0 +1,4 @@ +mod nu; +mod to_bson; + +pub use to_bson::ToBSON; diff --git a/crates/nu_plugin_to_bson/src/main.rs b/crates/nu_plugin_to_bson/src/main.rs new file mode 100644 index 0000000000..2170b73e46 --- /dev/null +++ b/crates/nu_plugin_to_bson/src/main.rs @@ -0,0 +1,6 @@ +use nu_plugin::serve_plugin; +use nu_plugin_to_bson::ToBSON; + +fn main() { + serve_plugin(&mut ToBSON::new()) +} diff --git a/crates/nu_plugin_to_bson/src/nu/mod.rs b/crates/nu_plugin_to_bson/src/nu/mod.rs new file mode 100644 index 0000000000..d732183f15 --- /dev/null +++ b/crates/nu_plugin_to_bson/src/nu/mod.rs @@ -0,0 +1,25 @@ +#[cfg(test)] +mod tests; + +use crate::ToBSON; +use nu_errors::ShellError; +use nu_plugin::Plugin; +use nu_protocol::{ReturnValue, Signature, Value}; +use nu_source::Tag; + +impl Plugin for ToBSON { + fn config(&mut self) -> Result { + Ok(Signature::build("to bson") + .desc("Convert table into .bson binary") + .filter()) + } + + fn filter(&mut self, input: Value) -> Result, ShellError> { + self.state.push(input); + Ok(vec![]) + } + + fn end_filter(&mut self) -> Result, ShellError> { + Ok(crate::to_bson::to_bson(self.state.clone(), Tag::unknown())) + } +} diff --git a/crates/nu_plugin_to_bson/src/nu/tests.rs b/crates/nu_plugin_to_bson/src/nu/tests.rs new file mode 100644 index 0000000000..d14c19bee3 --- /dev/null +++ b/crates/nu_plugin_to_bson/src/nu/tests.rs @@ -0,0 +1 @@ +mod integration {} diff --git a/crates/nu-cli/src/commands/to_bson.rs b/crates/nu_plugin_to_bson/src/to_bson.rs similarity index 88% rename from crates/nu-cli/src/commands/to_bson.rs rename to crates/nu_plugin_to_bson/src/to_bson.rs index f061103797..64fde09871 100644 --- a/crates/nu-cli/src/commands/to_bson.rs +++ b/crates/nu_plugin_to_bson/src/to_bson.rs @@ -1,39 +1,21 @@ -use crate::commands::WholeStreamCommand; -use crate::prelude::*; use bson::{encode_document, oid::ObjectId, spec::BinarySubtype, Bson, Document}; use nu_errors::{CoerceInto, ShellError}; use nu_protocol::{ - Dictionary, Primitive, ReturnSuccess, Signature, SpannedTypeName, UnspannedPathMember, + Dictionary, Primitive, ReturnSuccess, ReturnValue, SpannedTypeName, UnspannedPathMember, UntaggedValue, Value, }; +use nu_source::{Tag, TaggedItem}; +use num_traits::ToPrimitive; use std::convert::TryInto; -pub struct ToBSON; +#[derive(Default)] +pub struct ToBSON { + pub state: Vec, +} -#[async_trait] -impl WholeStreamCommand for ToBSON { - fn name(&self) -> &str { - "to bson" - } - - fn signature(&self) -> Signature { - Signature::build("to bson") - } - - fn usage(&self) -> &str { - "Convert table into .bson text." - } - - async fn run( - &self, - args: CommandArgs, - registry: &CommandRegistry, - ) -> Result { - to_bson(args, registry).await - } - - fn is_binary(&self) -> bool { - true +impl ToBSON { + pub fn new() -> ToBSON { + ToBSON { state: vec![] } } } @@ -261,17 +243,9 @@ fn bson_value_to_bytes(bson: Bson, tag: Tag) -> Result, ShellError> { Ok(out) } -async fn to_bson( - args: CommandArgs, - registry: &CommandRegistry, -) -> Result { - let registry = registry.clone(); - let args = args.evaluate_once(®istry).await?; - let name_tag = args.name_tag(); +pub fn to_bson(input: Vec, name_tag: Tag) -> Vec { let name_span = name_tag.span; - let input: Vec = args.input.collect().await; - let to_process_input = match input.len() { x if x > 1 => { let tag = input[0].tag.clone(); @@ -284,13 +258,14 @@ async fn to_bson( _ => vec![], }; - Ok(futures::stream::iter(to_process_input.into_iter().map( - move |value| match value_to_bson_value(&value) { + to_process_input + .into_iter() + .map(move |value| match value_to_bson_value(&value) { Ok(bson_value) => { let value_span = value.tag.span; match bson_value_to_bytes(bson_value, name_tag.clone()) { - Ok(x) => ReturnSuccess::value(UntaggedValue::binary(x).into_value(&name_tag)), + Ok(x) => ReturnSuccess::value(UntaggedValue::binary(x).into_value(name_span)), _ => Err(ShellError::labeled_error_with_secondary( "Expected a table with BSON-compatible structure from pipeline", "requires BSON-compatible input", @@ -305,19 +280,6 @@ async fn to_bson( "requires BSON-compatible input", &name_tag, )), - }, - )) - .to_output_stream()) -} - -#[cfg(test)] -mod tests { - use super::ToBSON; - - #[test] - fn examples_work_as_expected() { - use crate::examples::test as test_examples; - - test_examples(ToBSON {}) - } + }) + .collect() } diff --git a/crates/nu_plugin_to_sqlite/Cargo.toml b/crates/nu_plugin_to_sqlite/Cargo.toml new file mode 100644 index 0000000000..1de0b739ff --- /dev/null +++ b/crates/nu_plugin_to_sqlite/Cargo.toml @@ -0,0 +1,27 @@ +[package] +authors = ["The Nu Project Contributors"] +description = "A converter plugin to the bson format for Nushell" +edition = "2018" +license = "MIT" +name = "nu_plugin_to_sqlite" +version = "0.16.1" + +[lib] +doctest = false + +[dependencies] +hex = "0.4.2" +nu-errors = {path = "../nu-errors", version = "0.16.1"} +nu-plugin = {path = "../nu-plugin", version = "0.16.1"} +nu-protocol = {path = "../nu-protocol", version = "0.16.1"} +nu-source = {path = "../nu-source", version = "0.16.1"} +nu-value-ext = {path = "../nu-value-ext", version = "0.16.1"} +num-traits = "0.2.12" +tempfile = "3.1.0" + +[dependencies.rusqlite] +features = ["bundled", "blob"] +version = "0.23.1" + +[build-dependencies] +nu-build = {version = "0.16.1", path = "../nu-build"} diff --git a/crates/nu_plugin_to_sqlite/build.rs b/crates/nu_plugin_to_sqlite/build.rs new file mode 100644 index 0000000000..b7511cfc6a --- /dev/null +++ b/crates/nu_plugin_to_sqlite/build.rs @@ -0,0 +1,3 @@ +fn main() -> Result<(), Box> { + nu_build::build() +} diff --git a/crates/nu_plugin_to_sqlite/src/lib.rs b/crates/nu_plugin_to_sqlite/src/lib.rs new file mode 100644 index 0000000000..3e814f55b7 --- /dev/null +++ b/crates/nu_plugin_to_sqlite/src/lib.rs @@ -0,0 +1,4 @@ +mod nu; +mod to_sqlite; + +pub use to_sqlite::ToSqlite; diff --git a/crates/nu_plugin_to_sqlite/src/main.rs b/crates/nu_plugin_to_sqlite/src/main.rs new file mode 100644 index 0000000000..5f69232256 --- /dev/null +++ b/crates/nu_plugin_to_sqlite/src/main.rs @@ -0,0 +1,6 @@ +use nu_plugin::serve_plugin; +use nu_plugin_to_sqlite::ToSqlite; + +fn main() { + serve_plugin(&mut ToSqlite::new()) +} diff --git a/crates/nu_plugin_to_sqlite/src/nu/mod.rs b/crates/nu_plugin_to_sqlite/src/nu/mod.rs new file mode 100644 index 0000000000..08288db45a --- /dev/null +++ b/crates/nu_plugin_to_sqlite/src/nu/mod.rs @@ -0,0 +1,25 @@ +#[cfg(test)] +mod tests; + +use crate::ToSqlite; +use nu_errors::ShellError; +use nu_plugin::Plugin; +use nu_protocol::{ReturnValue, Signature, Value}; +use nu_source::Tag; + +impl Plugin for ToSqlite { + fn config(&mut self) -> Result { + Ok(Signature::build("to sqlite") + .desc("Convert table into sqlite binary") + .filter()) + } + + fn filter(&mut self, input: Value) -> Result, ShellError> { + self.state.push(input); + Ok(vec![]) + } + + fn end_filter(&mut self) -> Result, ShellError> { + crate::to_sqlite::to_sqlite(self.state.clone(), Tag::unknown()) + } +} diff --git a/crates/nu_plugin_to_sqlite/src/nu/tests.rs b/crates/nu_plugin_to_sqlite/src/nu/tests.rs new file mode 100644 index 0000000000..d14c19bee3 --- /dev/null +++ b/crates/nu_plugin_to_sqlite/src/nu/tests.rs @@ -0,0 +1 @@ +mod integration {} diff --git a/crates/nu-cli/src/commands/to_sqlite.rs b/crates/nu_plugin_to_sqlite/src/to_sqlite.rs similarity index 73% rename from crates/nu-cli/src/commands/to_sqlite.rs rename to crates/nu_plugin_to_sqlite/src/to_sqlite.rs index 1e30edf902..fb79be837b 100644 --- a/crates/nu-cli/src/commands/to_sqlite.rs +++ b/crates/nu_plugin_to_sqlite/src/to_sqlite.rs @@ -1,69 +1,20 @@ -use crate::commands::WholeStreamCommand; -use crate::prelude::*; use hex::encode; use nu_errors::ShellError; -use nu_protocol::{Dictionary, Primitive, ReturnSuccess, Signature, UntaggedValue, Value}; +use nu_protocol::{Dictionary, Primitive, ReturnSuccess, ReturnValue, UntaggedValue, Value}; +use nu_source::Tag; use rusqlite::{Connection, NO_PARAMS}; use std::io::Read; -pub struct ToSQLite; - -#[async_trait] -impl WholeStreamCommand for ToSQLite { - fn name(&self) -> &str { - "to sqlite" - } - - fn signature(&self) -> Signature { - Signature::build("to sqlite") - } - - fn usage(&self) -> &str { - "Convert table to sqlite .db binary data" - } - - async fn run( - &self, - args: CommandArgs, - registry: &CommandRegistry, - ) -> Result { - to_sqlite(args, registry).await - } - - fn is_binary(&self) -> bool { - true - } +#[derive(Default)] +pub struct ToSqlite { + pub state: Vec, } -pub struct ToDB; - -#[async_trait] -impl WholeStreamCommand for ToDB { - fn name(&self) -> &str { - "to db" - } - - fn signature(&self) -> Signature { - Signature::build("to db") - } - - fn usage(&self) -> &str { - "Convert table to db data" - } - - async fn run( - &self, - args: CommandArgs, - registry: &CommandRegistry, - ) -> Result { - to_sqlite(args, registry).await - } - - fn is_binary(&self) -> bool { - true +impl ToSqlite { + pub fn new() -> ToSqlite { + ToSqlite { state: vec![] } } } - fn comma_concat(acc: String, current: String) -> String { if acc == "" { current @@ -183,9 +134,6 @@ fn sqlite_input_stream_to_bytes(values: Vec) -> Result (), Err(e) => { - outln!("{}", create); - outln!("{}", insert); - outln!("{:?}", e); return Err(std::io::Error::new(std::io::ErrorKind::Other, e)); } } @@ -203,17 +151,9 @@ fn sqlite_input_stream_to_bytes(values: Vec) -> Result Result { - let registry = registry.clone(); - let args = args.evaluate_once(®istry).await?; - let name_tag = args.name_tag(); - let input: Vec = args.input.collect().await; - +pub fn to_sqlite(input: Vec, name_tag: Tag) -> Result, ShellError> { match sqlite_input_stream_to_bytes(input) { - Ok(out) => Ok(OutputStream::one(ReturnSuccess::value(out))), + Ok(out) => Ok(vec![ReturnSuccess::value(out)]), _ => Err(ShellError::labeled_error( "Expected a table with SQLite-compatible structure from pipeline", "requires SQLite-compatible input", @@ -221,15 +161,3 @@ async fn to_sqlite( )), } } - -#[cfg(test)] -mod tests { - use super::ToSQLite; - - #[test] - fn examples_work_as_expected() { - use crate::examples::test as test_examples; - - test_examples(ToSQLite {}) - } -} diff --git a/tests/plugins/mod.rs b/tests/plugins/mod.rs index e3d59b2205..23310d10ba 100644 --- a/tests/plugins/mod.rs +++ b/tests/plugins/mod.rs @@ -1 +1,2 @@ +#[cfg(features = "inc")] mod core_inc; diff --git a/tests/shell/pipeline/commands/external.rs b/tests/shell/pipeline/commands/external.rs index 4d1a17f4c7..d82009291b 100644 --- a/tests/shell/pipeline/commands/external.rs +++ b/tests/shell/pipeline/commands/external.rs @@ -1,5 +1,6 @@ use nu_test_support::nu; +#[cfg(feature = "which")] #[test] fn shows_error_for_command_not_found() { let actual = nu!( @@ -10,6 +11,7 @@ fn shows_error_for_command_not_found() { assert!(actual.err.contains("Command not found")); } +#[cfg(feature = "which")] #[test] fn shows_error_for_command_not_found_in_pipeline() { let actual = nu!( @@ -20,6 +22,7 @@ fn shows_error_for_command_not_found_in_pipeline() { assert!(actual.err.contains("Command not found")); } +#[cfg(feature = "which")] #[test] fn automatically_change_directory() { use nu_test_support::playground::Playground; diff --git a/tests/shell/pipeline/commands/internal.rs b/tests/shell/pipeline/commands/internal.rs index a5436e1cf8..6b23690540 100644 --- a/tests/shell/pipeline/commands/internal.rs +++ b/tests/shell/pipeline/commands/internal.rs @@ -1,4 +1,5 @@ use nu_test_support::fs::Stub::EmptyFile; +#[cfg(feature = "which")] use nu_test_support::fs::Stub::FileWithContent; use nu_test_support::fs::Stub::FileWithContentToBeTrimmed; use nu_test_support::nu; @@ -37,6 +38,7 @@ fn takes_rows_of_nu_value_strings_and_pipes_it_to_stdin_of_external() { }) } +#[cfg(feature = "which")] #[test] fn autoenv() { Playground::setup("autoenv_test", |dirs, sandbox| { @@ -379,6 +381,7 @@ fn string_interpolation_with_it_column_path() { assert_eq!(actual.out, "sammie"); } +#[cfg(feature = "which")] #[test] fn argument_invocation_reports_errors() { let actual = nu!(