Add wasm support (#2199)

* Working towards a PoC for wasm

* Move bson and sqlite to plugins

* proof of concept now working

* tests are green

* Add CI test for --no-default-features

* Fix some tests

* Fix clippy and windows build

* More fixes

* Fix the windows build

* Fix the windows test
This commit is contained in:
Jonathan Turner 2020-07-18 13:59:23 +12:00 committed by GitHub
parent dbe0effd67
commit d8594a62c2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
59 changed files with 781 additions and 448 deletions

View file

@ -6,6 +6,9 @@ strategy:
linux-stable: linux-stable:
image: ubuntu-18.04 image: ubuntu-18.04
style: 'unflagged' style: 'unflagged'
linux-minimal:
image: ubuntu-18.04
style: 'minimal'
macos-stable: macos-stable:
image: macos-10.14 image: macos-10.14
style: 'unflagged' 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 - 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') condition: eq(variables['style'], 'canary')
displayName: Check clippy lints 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 - bash: cargo fmt --all -- --check
condition: eq(variables['style'], 'fmt') condition: eq(variables['style'], 'fmt')
displayName: Lint displayName: Lint

66
Cargo.lock generated
View file

@ -2464,6 +2464,8 @@ dependencies = [
"nu-value-ext", "nu-value-ext",
"nu_plugin_binaryview", "nu_plugin_binaryview",
"nu_plugin_fetch", "nu_plugin_fetch",
"nu_plugin_from_bson",
"nu_plugin_from_sqlite",
"nu_plugin_inc", "nu_plugin_inc",
"nu_plugin_match", "nu_plugin_match",
"nu_plugin_post", "nu_plugin_post",
@ -2471,6 +2473,8 @@ dependencies = [
"nu_plugin_start", "nu_plugin_start",
"nu_plugin_sys", "nu_plugin_sys",
"nu_plugin_textview", "nu_plugin_textview",
"nu_plugin_to_bson",
"nu_plugin_to_sqlite",
"nu_plugin_tree", "nu_plugin_tree",
"pretty_env_logger", "pretty_env_logger",
"semver 0.10.0", "semver 0.10.0",
@ -2501,7 +2505,6 @@ dependencies = [
"async-trait", "async-trait",
"base64 0.12.3", "base64 0.12.3",
"bigdecimal", "bigdecimal",
"bson",
"byte-unit", "byte-unit",
"bytes 0.5.5", "bytes 0.5.5",
"calamine", "calamine",
@ -2752,6 +2755,37 @@ dependencies = [
"url", "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]] [[package]]
name = "nu_plugin_inc" name = "nu_plugin_inc"
version = "0.16.1" version = "0.16.1"
@ -2857,6 +2891,36 @@ dependencies = [
"url", "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]] [[package]]
name = "nu_plugin_tree" name = "nu_plugin_tree"
version = "0.16.1" version = "0.16.1"

View file

@ -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-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_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_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_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_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} 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_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_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_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} nu_plugin_tree = {version = "0.16.1", path = "./crates/nu_plugin_tree", optional = true}
crossterm = {version = "0.17.5", optional = true} crossterm = {version = "0.17.5", optional = true}
@ -58,8 +62,20 @@ serde = {version = "1.0.110", features = ["derive"]}
toml = "0.5.6" toml = "0.5.6"
[features] [features]
default = ["sys", "ps", "textview", "inc"] default = [
stable = ["default", "binaryview", "match", "tree", "post", "fetch", "clipboard-cli", "trash-support", "start"] "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 # Default
inc = ["semver", "nu_plugin_inc"] inc = ["semver", "nu_plugin_inc"]
@ -69,16 +85,25 @@ textview = ["crossterm", "syntect", "url", "nu_plugin_textview"]
# Stable # Stable
binaryview = ["nu_plugin_binaryview"] binaryview = ["nu_plugin_binaryview"]
bson = ["nu_plugin_from_bson", "nu_plugin_to_bson"]
fetch = ["nu_plugin_fetch"] fetch = ["nu_plugin_fetch"]
match = ["nu_plugin_match"] match = ["nu_plugin_match"]
post = ["nu_plugin_post"] post = ["nu_plugin_post"]
sqlite = ["nu_plugin_from_sqlite", "nu_plugin_to_sqlite"]
start = ["nu_plugin_start"] start = ["nu_plugin_start"]
trace = ["nu-parser/trace"] trace = ["nu-parser/trace"]
tree = ["nu_plugin_tree"] tree = ["nu_plugin_tree"]
clipboard-cli = ["nu-cli/clipboard-cli"] 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"] 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 # Core plugins that ship with `cargo install nu` by default
# Currently, Cargo limits us to installing only one binary # Currently, Cargo limits us to installing only one binary

View file

@ -25,7 +25,6 @@ async-recursion = "0.3.1"
async-trait = "0.1.36" async-trait = "0.1.36"
base64 = "0.12.3" base64 = "0.12.3"
bigdecimal = {version = "0.1.2", features = ["serde"]} bigdecimal = {version = "0.1.2", features = ["serde"]}
bson = {version = "0.14.1", features = ["decimal128"]}
byte-unit = "3.1.3" byte-unit = "3.1.3"
bytes = "0.5.5" bytes = "0.5.5"
calamine = "0.16" calamine = "0.16"
@ -33,10 +32,10 @@ chrono = {version = "0.4.11", features = ["serde"]}
clap = "2.33.1" clap = "2.33.1"
codespan-reporting = "0.9.5" codespan-reporting = "0.9.5"
csv = "1.1" csv = "1.1"
ctrlc = "3.1.4" ctrlc = {version = "3.1.4", optional = true}
derive-new = "0.5.8" derive-new = "0.5.8"
directories = "2.0.2" directories = {version = "2.0.2", optional = true}
dirs = "2.0.2" dirs = {version = "2.0.2", optional = true}
dunce = "1.0.1" dunce = "1.0.1"
eml-parser = "0.1.0" eml-parser = "0.1.0"
filesize = "0.2.0" filesize = "0.2.0"
@ -44,12 +43,12 @@ futures = {version = "0.3", features = ["compat", "io-compat"]}
futures-util = "0.3.5" futures-util = "0.3.5"
futures_codec = "0.4" futures_codec = "0.4"
getset = "0.1.1" 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" glob = "0.3.0"
hex = "0.4" hex = "0.4"
htmlescape = "0.3.1" htmlescape = "0.3.1"
ical = "0.6.*" ical = "0.6.*"
ichwh = "0.3.4" ichwh = {version = "0.3.4", optional = true}
indexmap = {version = "1.4.0", features = ["serde-1"]} indexmap = {version = "1.4.0", features = ["serde-1"]}
itertools = "0.9.0" itertools = "0.9.0"
log = "0.4.8" log = "0.4.8"
@ -62,7 +61,7 @@ parking_lot = "0.11.0"
pin-utils = "0.1.0" pin-utils = "0.1.0"
pretty-hex = "0.1.1" pretty-hex = "0.1.1"
pretty_env_logger = "0.4.0" pretty_env_logger = "0.4.0"
ptree = {version = "0.2"} ptree = {version = "0.2", optional = true}
query_interface = "0.3.5" query_interface = "0.3.5"
rand = "0.7" rand = "0.7"
regex = "1" regex = "1"
@ -79,20 +78,20 @@ sha2 = "0.9.1"
shellexpand = "2.0.0" shellexpand = "2.0.0"
strip-ansi-escapes = "0.1.0" strip-ansi-escapes = "0.1.0"
tempfile = "3.1.0" tempfile = "3.1.0"
term = "0.5.2" term = {version = "0.5.2", optional = true}
term_size = "0.3.2" term_size = "0.3.2"
termcolor = "1.1.0" termcolor = "1.1.0"
toml = "0.5.6" toml = "0.5.6"
typetag = "0.1.5" typetag = "0.1.5"
umask = "1.0.0" umask = "1.0.0"
unicode-xid = "0.2.1" unicode-xid = "0.2.1"
uuid_crate = {package = "uuid", version = "0.8.1", features = ["v4"]} uuid_crate = {package = "uuid", version = "0.8.1", features = ["v4"], optional = true}
which = "4.0.1" which = {version = "4.0.1", optional = true}
clipboard = {version = "0.5", optional = true} clipboard = {version = "0.5", optional = true}
encoding_rs = "0.8.23" encoding_rs = "0.8.23"
rayon = "1.3.1" rayon = "1.3.1"
starship = "0.43.0" starship = {version = "0.43.0", optional = true}
trash = {version = "1.0.1", optional = true} trash = {version = "1.0.1", optional = true}
[target.'cfg(unix)'.dependencies] [target.'cfg(unix)'.dependencies]
@ -106,6 +105,7 @@ users = "0.10.0"
[dependencies.rusqlite] [dependencies.rusqlite]
features = ["bundled", "blob"] features = ["bundled", "blob"]
optional = true
version = "0.23.1" version = "0.23.1"
[build-dependencies] [build-dependencies]
@ -116,7 +116,7 @@ quickcheck = "0.9"
quickcheck_macros = "0.9" quickcheck_macros = "0.9"
[features] [features]
stable = []
# starship-prompt = ["starship"]
clipboard-cli = ["clipboard"] clipboard-cli = ["clipboard"]
stable = []
starship-prompt = ["starship"]
trash-support = ["trash"] trash-support = ["trash"]

View file

@ -12,6 +12,8 @@ use futures_codec::FramedRead;
use nu_errors::{ProximateShellError, ShellDiagnostic, ShellError}; use nu_errors::{ProximateShellError, ShellDiagnostic, ShellError};
use nu_protocol::hir::{ClassifiedCommand, Expression, InternalCommand, Literal, NamedArguments}; use nu_protocol::hir::{ClassifiedCommand, Expression, InternalCommand, Literal, NamedArguments};
use nu_protocol::{Primitive, ReturnSuccess, Signature, UntaggedValue, Value}; use nu_protocol::{Primitive, ReturnSuccess, Signature, UntaggedValue, Value};
#[allow(unused)]
use nu_source::Tagged;
use log::{debug, trace}; use log::{debug, trace};
use rustyline::error::ReadlineError; use rustyline::error::ReadlineError;
@ -374,12 +376,9 @@ pub fn create_default_context(
whole_stream_command(MathVariance), whole_stream_command(MathVariance),
// File format output // File format output
whole_stream_command(To), whole_stream_command(To),
whole_stream_command(ToBSON),
whole_stream_command(ToCSV), whole_stream_command(ToCSV),
whole_stream_command(ToHTML), whole_stream_command(ToHTML),
whole_stream_command(ToJSON), whole_stream_command(ToJSON),
whole_stream_command(ToSQLite),
whole_stream_command(ToDB),
whole_stream_command(ToMarkdown), whole_stream_command(ToMarkdown),
whole_stream_command(ToTOML), whole_stream_command(ToTOML),
whole_stream_command(ToTSV), whole_stream_command(ToTSV),
@ -392,11 +391,8 @@ pub fn create_default_context(
whole_stream_command(FromTSV), whole_stream_command(FromTSV),
whole_stream_command(FromSSV), whole_stream_command(FromSSV),
whole_stream_command(FromINI), whole_stream_command(FromINI),
whole_stream_command(FromBSON),
whole_stream_command(FromJSON), whole_stream_command(FromJSON),
whole_stream_command(FromODS), whole_stream_command(FromODS),
whole_stream_command(FromDB),
whole_stream_command(FromSQLite),
whole_stream_command(FromTOML), whole_stream_command(FromTOML),
whole_stream_command(FromURL), whole_stream_command(FromURL),
whole_stream_command(FromXLSX), whole_stream_command(FromXLSX),
@ -411,6 +407,7 @@ pub fn create_default_context(
whole_stream_command(Random), whole_stream_command(Random),
whole_stream_command(RandomBool), whole_stream_command(RandomBool),
whole_stream_command(RandomDice), whole_stream_command(RandomDice),
#[cfg(feature = "uuid_crate")]
whole_stream_command(RandomUUID), whole_stream_command(RandomUUID),
]); ]);
@ -432,6 +429,8 @@ pub async fn run_vec_of_pipelines(
let _ = crate::load_plugins(&mut context); let _ = crate::load_plugins(&mut context);
#[cfg(feature = "ctrlc")]
{
let cc = context.ctrl_c.clone(); let cc = context.ctrl_c.clone();
ctrlc::set_handler(move || { ctrlc::set_handler(move || {
@ -442,6 +441,7 @@ pub async fn run_vec_of_pipelines(
if context.ctrl_c.load(Ordering::SeqCst) { if context.ctrl_c.load(Ordering::SeqCst) {
context.ctrl_c.store(false, Ordering::SeqCst); context.ctrl_c.store(false, Ordering::SeqCst);
} }
}
// before we start up, let's run our startup commands // before we start up, let's run our startup commands
if let Ok(config) = crate::data::config::config(Tag::unknown()) { if let Ok(config) = crate::data::config::config(Tag::unknown()) {
@ -557,11 +557,15 @@ pub async fn cli(
// we are ok if history does not exist // we are ok if history does not exist
let _ = rl.load_history(&History::path()); let _ = rl.load_history(&History::path());
#[cfg(feature = "ctrlc")]
{
let cc = context.ctrl_c.clone(); let cc = context.ctrl_c.clone();
ctrlc::set_handler(move || { ctrlc::set_handler(move || {
cc.store(true, Ordering::SeqCst); cc.store(true, Ordering::SeqCst);
}) })
.expect("Error setting Ctrl-C handler"); .expect("Error setting Ctrl-C handler");
}
let mut ctrlcbreak = false; let mut ctrlcbreak = false;
// before we start up, let's run our startup commands // before we start up, let's run our startup commands
@ -662,6 +666,8 @@ pub async fn cli(
let colored_prompt = { let colored_prompt = {
if use_starship { if use_starship {
#[cfg(feature = "starship")]
{
std::env::set_var("STARSHIP_SHELL", ""); std::env::set_var("STARSHIP_SHELL", "");
std::env::set_var("PWD", &cwd); std::env::set_var("PWD", &cwd);
let mut starship_context = let mut starship_context =
@ -677,6 +683,18 @@ pub async fn cli(
_ => {} _ => {}
}; };
starship::print::get_prompt(starship_context) 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") { } else if let Some(prompt) = config.get("prompt") {
let prompt_line = prompt.as_string()?; let prompt_line = prompt.as_string()?;
@ -829,6 +847,7 @@ fn chomp_newline(s: &str) -> &str {
} }
} }
#[derive(Debug)]
pub enum LineResult { pub enum LineResult {
Success(String), Success(String),
Error(String, ShellError), Error(String, ShellError),
@ -836,6 +855,36 @@ pub enum LineResult {
Break, Break,
} }
pub async fn parse_and_eval(line: &str, ctx: &mut Context) -> Result<String, ShellError> {
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 /// 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( pub async fn process_line(
readline: Result<String, ReadlineError>, readline: Result<String, ReadlineError>,
@ -912,7 +961,7 @@ pub async fn process_line(
.unwrap_or(true) .unwrap_or(true)
&& canonicalize(ctx.shell_manager.path(), name).is_ok() && canonicalize(ctx.shell_manager.path(), name).is_ok()
&& Path::new(&name).is_dir() && 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 // Here we work differently if we're in Windows because of the expected Windows behavior
#[cfg(windows)] #[cfg(windows)]

View file

@ -40,14 +40,12 @@ pub(crate) mod exit;
pub(crate) mod first; pub(crate) mod first;
pub(crate) mod format; pub(crate) mod format;
pub(crate) mod from; pub(crate) mod from;
pub(crate) mod from_bson;
pub(crate) mod from_csv; pub(crate) mod from_csv;
pub(crate) mod from_eml; pub(crate) mod from_eml;
pub(crate) mod from_ics; pub(crate) mod from_ics;
pub(crate) mod from_ini; pub(crate) mod from_ini;
pub(crate) mod from_json; pub(crate) mod from_json;
pub(crate) mod from_ods; pub(crate) mod from_ods;
pub(crate) mod from_sqlite;
pub(crate) mod from_ssv; pub(crate) mod from_ssv;
pub(crate) mod from_toml; pub(crate) mod from_toml;
pub(crate) mod from_tsv; pub(crate) mod from_tsv;
@ -104,12 +102,10 @@ pub(crate) mod str_;
pub(crate) mod table; pub(crate) mod table;
pub(crate) mod tags; pub(crate) mod tags;
pub(crate) mod to; pub(crate) mod to;
pub(crate) mod to_bson;
pub(crate) mod to_csv; pub(crate) mod to_csv;
pub(crate) mod to_html; pub(crate) mod to_html;
pub(crate) mod to_json; pub(crate) mod to_json;
pub(crate) mod to_md; pub(crate) mod to_md;
pub(crate) mod to_sqlite;
pub(crate) mod to_toml; pub(crate) mod to_toml;
pub(crate) mod to_tsv; pub(crate) mod to_tsv;
pub(crate) mod to_url; pub(crate) mod to_url;
@ -168,15 +164,12 @@ pub(crate) use exit::Exit;
pub(crate) use first::First; pub(crate) use first::First;
pub(crate) use format::Format; pub(crate) use format::Format;
pub(crate) use from::From; pub(crate) use from::From;
pub(crate) use from_bson::FromBSON;
pub(crate) use from_csv::FromCSV; pub(crate) use from_csv::FromCSV;
pub(crate) use from_eml::FromEML; pub(crate) use from_eml::FromEML;
pub(crate) use from_ics::FromIcs; pub(crate) use from_ics::FromIcs;
pub(crate) use from_ini::FromINI; pub(crate) use from_ini::FromINI;
pub(crate) use from_json::FromJSON; pub(crate) use from_json::FromJSON;
pub(crate) use from_ods::FromODS; 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_ssv::FromSSV;
pub(crate) use from_toml::FromTOML; pub(crate) use from_toml::FromTOML;
pub(crate) use from_tsv::FromTSV; pub(crate) use from_tsv::FromTSV;
@ -213,7 +206,9 @@ pub(crate) use pivot::Pivot;
pub(crate) use prepend::Prepend; pub(crate) use prepend::Prepend;
pub(crate) use prev::Previous; pub(crate) use prev::Previous;
pub(crate) use pwd::Pwd; 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 range::Range;
pub(crate) use reject::Reject; pub(crate) use reject::Reject;
pub(crate) use rename::Rename; pub(crate) use rename::Rename;
@ -236,13 +231,10 @@ pub(crate) use str_::{
pub(crate) use table::Table; pub(crate) use table::Table;
pub(crate) use tags::Tags; pub(crate) use tags::Tags;
pub(crate) use to::To; pub(crate) use to::To;
pub(crate) use to_bson::ToBSON;
pub(crate) use to_csv::ToCSV; pub(crate) use to_csv::ToCSV;
pub(crate) use to_html::ToHTML; pub(crate) use to_html::ToHTML;
pub(crate) use to_json::ToJSON; pub(crate) use to_json::ToJSON;
pub(crate) use to_md::ToMarkdown; 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_toml::ToTOML;
pub(crate) use to_tsv::ToTSV; pub(crate) use to_tsv::ToTSV;
pub(crate) use to_url::ToURL; pub(crate) use to_url::ToURL;

View file

@ -26,7 +26,7 @@ pub(crate) async fn run_external_command(
) -> Result<InputStream, ShellError> { ) -> Result<InputStream, ShellError> {
trace!(target: "nu::run::external", "-> {}", command.name); 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( return Err(ShellError::labeled_error(
"Command not found", "Command not found",
"command not found", "command not found",
@ -70,7 +70,18 @@ async fn run_with_stdin(
let process_args = command_args let process_args = command_args
.iter() .iter()
.map(|arg| { .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))] #[cfg(not(windows))]
{ {
@ -407,13 +418,19 @@ fn spawn(
} }
} }
async fn did_find_command(name: &str) -> bool { pub fn did_find_command(#[allow(unused)] name: &str) -> bool {
#[cfg(not(windows))] #[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() which::which(name).is_ok()
} }
#[cfg(windows)] #[cfg(all(feature = "which", windows))]
{ {
if which::which(name).is_ok() { if which::which(name).is_ok() {
true true
@ -483,11 +500,17 @@ fn shell_os_paths() -> Vec<std::path::PathBuf> {
mod tests { mod tests {
use super::{ use super::{
add_quotes, argument_contains_whitespace, argument_is_quoted, expand_tilde, remove_quotes, 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; use futures::executor::block_on;
#[cfg(feature = "which")]
use nu_errors::ShellError; use nu_errors::ShellError;
#[cfg(feature = "which")]
use nu_protocol::Scope; use nu_protocol::Scope;
#[cfg(feature = "which")]
use nu_test_support::commands::ExternalBuilder; use nu_test_support::commands::ExternalBuilder;
// async fn read(mut stream: OutputStream) -> Option<Value> { // async fn read(mut stream: OutputStream) -> Option<Value> {
@ -503,6 +526,7 @@ mod tests {
// } // }
// } // }
#[cfg(feature = "which")]
async fn non_existent_run() -> Result<(), ShellError> { async fn non_existent_run() -> Result<(), ShellError> {
let cmd = ExternalBuilder::for_name("i_dont_exist.exe").build(); let cmd = ExternalBuilder::for_name("i_dont_exist.exe").build();
@ -542,6 +566,7 @@ mod tests {
// block_on(failure_run()) // block_on(failure_run())
// } // }
#[cfg(feature = "which")]
#[test] #[test]
fn identifies_command_not_found() -> Result<(), ShellError> { fn identifies_command_not_found() -> Result<(), ShellError> {
block_on(non_existent_run()) block_on(non_existent_run())

View file

@ -2,10 +2,12 @@ pub mod command;
pub mod bool; pub mod bool;
pub mod dice; pub mod dice;
#[cfg(feature = "uuid_crate")]
pub mod uuid; pub mod uuid;
pub use command::Command as Random; pub use command::Command as Random;
pub use self::bool::SubCommand as RandomBool; pub use self::bool::SubCommand as RandomBool;
pub use dice::SubCommand as RandomDice; pub use dice::SubCommand as RandomDice;
#[cfg(feature = "uuid_crate")]
pub use uuid::SubCommand as RandomUUID; pub use uuid::SubCommand as RandomUUID;

View file

@ -157,7 +157,7 @@ async fn maybe_autocd_dir<'a>(cmd: &ExternalCommand, ctx: &mut Context) -> Optio
|| (cmd.args.is_empty() || (cmd.args.is_empty()
&& PathBuf::from(name).is_dir() && PathBuf::from(name).is_dir()
&& dunce::canonicalize(name).is_ok() && dunce::canonicalize(name).is_ok()
&& which::which(&name).is_err()) && !crate::commands::classified::external::did_find_command(&name))
{ {
Some(name) Some(name)
} else { } else {

View file

@ -60,6 +60,7 @@ macro_rules! entry_builtin {
}; };
} }
#[allow(unused)]
macro_rules! entry_path { macro_rules! entry_path {
($arg:expr, $path:expr, $tag:expr) => { ($arg:expr, $path:expr, $tag:expr) => {
entry( entry(
@ -99,6 +100,8 @@ async fn which(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputSt
} }
} }
#[cfg(feature = "ichwh")]
{
if let Ok(paths) = ichwh::which_all(&item).await { if let Ok(paths) = ichwh::which_all(&item).await {
for path in paths { for path in paths {
output.push(ReturnSuccess::value(entry_path!( output.push(ReturnSuccess::value(entry_path!(
@ -108,6 +111,7 @@ async fn which(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputSt
))); )));
} }
} }
}
if all { if all {
Ok(futures::stream::iter(output.into_iter()).to_output_stream()) Ok(futures::stream::iter(output.into_iter()).to_output_stream())

View file

@ -144,7 +144,7 @@ impl Context {
} }
} }
pub(crate) fn basic() -> Result<Context, Box<dyn Error>> { pub fn basic() -> Result<Context, Box<dyn Error>> {
let registry = CommandRegistry::new(); let registry = CommandRegistry::new();
#[cfg(windows)] #[cfg(windows)]

View file

@ -10,7 +10,6 @@ pub(crate) use nuconfig::NuConfig;
use crate::commands::from_toml::convert_toml_value_to_nu_value; use crate::commands::from_toml::convert_toml_value_to_nu_value;
use crate::commands::to_toml::value_to_toml_value; use crate::commands::to_toml::value_to_toml_value;
use crate::prelude::*; use crate::prelude::*;
use directories::ProjectDirs;
use indexmap::IndexMap; use indexmap::IndexMap;
use log::trace; use log::trace;
use nu_errors::ShellError; use nu_errors::ShellError;
@ -20,8 +19,25 @@ use std::fs::{self, OpenOptions};
use std::io; use std::io;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
#[cfg(feature = "directories")]
pub fn config_path() -> Result<PathBuf, ShellError> { pub fn config_path() -> Result<PathBuf, ShellError> {
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<PathBuf, ShellError> {
// FIXME: unsure if this should be error or a simple default
Ok(std::path::PathBuf::from("/"))
} }
pub fn default_path() -> Result<PathBuf, ShellError> { pub fn default_path() -> Result<PathBuf, ShellError> {
@ -39,21 +55,30 @@ pub fn default_path_for(file: &Option<PathBuf>) -> Result<PathBuf, ShellError> {
Ok(filename) Ok(filename)
} }
#[cfg(feature = "directories")]
pub fn user_data() -> Result<PathBuf, ShellError> { pub fn user_data() -> Result<PathBuf, ShellError> {
app_path("user data", ProjectDirs::data_local_dir) use directories::ProjectDirs;
}
fn app_path<F: FnOnce(&ProjectDirs) -> &Path>(display: &str, f: F) -> Result<PathBuf, ShellError> {
let dir = ProjectDirs::from("org", "nushell", "nu") let dir = ProjectDirs::from("org", "nushell", "nu")
.ok_or_else(|| ShellError::untagged_runtime_error("Couldn't find project directory"))?; .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| { 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) Ok(path)
} }
#[cfg(not(feature = "directories"))]
pub fn user_data() -> Result<PathBuf, ShellError> {
// FIXME: unsure if this should be error or a simple default
Ok(std::path::PathBuf::from("/"))
}
pub fn read( pub fn read(
tag: impl Into<Tag>, tag: impl Into<Tag>,
at: &Option<PathBuf>, at: &Option<PathBuf>,

View file

@ -6,9 +6,6 @@ use std::ffi::OsString;
use std::fmt::Debug; use std::fmt::Debug;
pub trait Host: Debug + Send { pub trait Host: Debug + Send {
fn out_terminal(&self) -> Option<Box<term::StdoutTerminal>>;
fn err_terminal(&self) -> Option<Box<term::StderrTerminal>>;
fn out_termcolor(&self) -> termcolor::StandardStream; fn out_termcolor(&self) -> termcolor::StandardStream;
fn err_termcolor(&self) -> termcolor::StandardStream; fn err_termcolor(&self) -> termcolor::StandardStream;
@ -24,14 +21,6 @@ pub trait Host: Debug + Send {
} }
impl Host for Box<dyn Host> { impl Host for Box<dyn Host> {
fn out_terminal(&self) -> Option<Box<term::StdoutTerminal>> {
(**self).out_terminal()
}
fn err_terminal(&self) -> Option<Box<term::StderrTerminal>> {
(**self).err_terminal()
}
fn stdout(&mut self, out: &str) { fn stdout(&mut self, out: &str) {
(**self).stdout(out) (**self).stdout(out)
} }
@ -73,14 +62,6 @@ impl Host for Box<dyn Host> {
pub struct BasicHost; pub struct BasicHost;
impl Host for BasicHost { impl Host for BasicHost {
fn out_terminal(&self) -> Option<Box<term::StdoutTerminal>> {
term::stdout()
}
fn err_terminal(&self) -> Option<Box<term::StderrTerminal>> {
term::stderr()
}
fn stdout(&mut self, out: &str) { fn stdout(&mut self, out: &str) {
match out { match out {
"\n" => outln!(""), "\n" => outln!(""),
@ -96,20 +77,41 @@ impl Host for BasicHost {
} }
fn vars(&mut self) -> Vec<(String, String)> { fn vars(&mut self) -> Vec<(String, String)> {
#[cfg(not(target_arch = "wasm32"))]
{
std::env::vars().collect::<Vec<_>>() std::env::vars().collect::<Vec<_>>()
} }
#[cfg(target_arch = "wasm32")]
{
vec![]
}
}
fn env_get(&mut self, key: OsString) -> Option<OsString> { fn env_get(&mut self, key: OsString) -> Option<OsString> {
#[cfg(not(target_arch = "wasm32"))]
{
std::env::var_os(key) std::env::var_os(key)
} }
#[cfg(target_arch = "wasm32")]
{
None
}
}
fn env_set(&mut self, key: OsString, value: OsString) { fn env_set(&mut self, key: OsString, value: OsString) {
#[cfg(not(target_arch = "wasm32"))]
{
std::env::set_var(key, value); std::env::set_var(key, value);
} }
}
fn env_rm(&mut self, key: OsString) { fn env_rm(&mut self, key: OsString) {
#[cfg(not(target_arch = "wasm32"))]
{
std::env::remove_var(key); std::env::remove_var(key);
} }
}
fn out_termcolor(&self) -> termcolor::StandardStream { fn out_termcolor(&self) -> termcolor::StandardStream {
termcolor::StandardStream::stdout(termcolor::ColorChoice::Auto) termcolor::StandardStream::stdout(termcolor::ColorChoice::Auto)
@ -145,14 +147,6 @@ impl FakeHost {
#[cfg(test)] #[cfg(test)]
impl Host for FakeHost { impl Host for FakeHost {
fn out_terminal(&self) -> Option<Box<term::StdoutTerminal>> {
None
}
fn err_terminal(&self) -> Option<Box<term::StderrTerminal>> {
None
}
fn stdout(&mut self, out: &str) { fn stdout(&mut self, out: &str) {
self.line_written = out.to_string(); self.line_written = out.to_string();
} }

View file

@ -32,7 +32,7 @@ pub fn nu(env: &IndexMap<String, String>, tag: impl Into<Tag>) -> Result<Value,
let path = std::env::current_dir()?; let path = std::env::current_dir()?;
nu_dict.insert_value("cwd", UntaggedValue::path(path).into_value(&tag)); nu_dict.insert_value("cwd", UntaggedValue::path(path).into_value(&tag));
if let Some(home) = dirs::home_dir() { if let Some(home) = crate::shell::filesystem_shell::homedir_if_possible() {
nu_dict.insert_value("home-dir", UntaggedValue::path(home).into_value(&tag)); nu_dict.insert_value("home-dir", UntaggedValue::path(home).into_value(&tag));
} }

View file

@ -1,6 +1,4 @@
use crate::prelude::*; use crate::prelude::*;
use git2::{Repository, RepositoryOpenFlags};
use std::ffi::OsString;
pub fn current_branch() -> Option<String> { pub fn current_branch() -> Option<String> {
if let Ok(config) = crate::data::config::config(Tag::unknown()) { if let Ok(config) = crate::data::config::config(Tag::unknown()) {
@ -13,6 +11,11 @@ pub fn current_branch() -> Option<String> {
}; };
if !use_starship { if !use_starship {
#[cfg(feature = "git2")]
{
use git2::{Repository, RepositoryOpenFlags};
use std::ffi::OsString;
let v: Vec<OsString> = vec![]; let v: Vec<OsString> = vec![];
match Repository::open_ext(".", RepositoryOpenFlags::empty(), v) { match Repository::open_ext(".", RepositoryOpenFlags::empty(), v) {
Ok(repo) => { Ok(repo) => {
@ -27,6 +30,11 @@ pub fn current_branch() -> Option<String> {
} }
_ => None, _ => None,
} }
}
#[cfg(not(feature = "git2"))]
{
None
}
} else { } else {
None None
} }

View file

@ -34,14 +34,14 @@ pub mod utils;
mod examples; mod examples;
pub use crate::cli::{ pub use crate::cli::{
cli, create_default_context, load_plugins, process_line, run_pipeline_standalone, cli, create_default_context, load_plugins, parse_and_eval, process_line,
run_vec_of_pipelines, LineResult, run_pipeline_standalone, run_vec_of_pipelines, LineResult,
}; };
pub use crate::commands::command::{ pub use crate::commands::command::{
whole_stream_command, CommandArgs, EvaluatedWholeStreamCommandArgs, WholeStreamCommand, whole_stream_command, CommandArgs, EvaluatedWholeStreamCommandArgs, WholeStreamCommand,
}; };
pub use crate::commands::help::get_help; 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::config;
pub use crate::data::dict::TaggedListBuilder; pub use crate::data::dict::TaggedListBuilder;
pub use crate::data::primitive; pub use crate::data::primitive;

View file

@ -3,8 +3,9 @@ use crate::context::CommandRegistry;
use crate::data::config; use crate::data::config;
use crate::prelude::*; use crate::prelude::*;
use derive_new::new; use derive_new::new;
#[cfg(windows)] #[cfg(all(windows, feature = "ichwh"))]
use ichwh::IchwhError; use ichwh::IchwhError;
#[cfg(all(windows, feature = "ichwh"))]
use ichwh::IchwhResult; use ichwh::IchwhResult;
use indexmap::set::IndexSet; use indexmap::set::IndexSet;
use rustyline::completion::{Completer, FilenameCompleter}; use rustyline::completion::{Completer, FilenameCompleter};
@ -225,35 +226,49 @@ impl NuCompleter {
} }
#[cfg(windows)] #[cfg(windows)]
fn is_executable(&self, file: &DirEntry) -> IchwhResult<bool> { fn is_executable(&self, file: &DirEntry) -> bool {
let file_type = file.metadata()?.file_type(); if let Ok(metadata) = file.metadata() {
let file_type = metadata.file_type();
// If the entry isn't a file, it cannot be executable // If the entry isn't a file, it cannot be executable
if !(file_type.is_file() || file_type.is_symlink()) { if !(file_type.is_file() || file_type.is_symlink()) {
return Ok(false); return false;
} }
if let Some(extension) = file.path().extension() { if let Some(extension) = file.path().extension() {
let exts = self.pathext()?; if let Ok(exts) = self.pathext() {
exts.iter()
Ok(exts .any(|ext| extension.to_string_lossy().eq_ignore_ascii_case(ext))
.iter()
.any(|ext| extension.to_string_lossy().eq_ignore_ascii_case(ext)))
} else { } else {
Ok(false) false
} }
} else {
false
}
} else {
false
}
}
#[cfg(target_arch = "wasm32")]
fn is_executable(&self, file: &DirEntry) -> bool {
false
} }
#[cfg(unix)] #[cfg(unix)]
fn is_executable(&self, file: &DirEntry) -> IchwhResult<bool> { fn is_executable(&self, file: &DirEntry) -> bool {
let metadata = file.metadata()?; let metadata = file.metadata();
if let Ok(metadata) = metadata {
let filetype = metadata.file_type(); let filetype = metadata.file_type();
let permissions = metadata.permissions(); let permissions = metadata.permissions();
// The file is executable if it is a directory or a symlink and the permissions are set for // The file is executable if it is a directory or a symlink and the permissions are set for
// owner, group, or other // owner, group, or other
Ok((filetype.is_file() || filetype.is_symlink()) && (permissions.mode() & 0o111 != 0)) (filetype.is_file() || filetype.is_symlink()) && (permissions.mode() & 0o111 != 0)
} else {
false
}
} }
fn find_path_executables(&self) -> Option<IndexSet<String>> { fn find_path_executables(&self) -> Option<IndexSet<String>> {
@ -264,7 +279,7 @@ impl NuCompleter {
for path in paths { for path in paths {
if let Ok(mut contents) = read_dir(path) { if let Ok(mut contents) = read_dir(path) {
while let Some(Ok(item)) = contents.next() { 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() { if let Ok(name) = item.file_name().into_string() {
executables.insert(name); executables.insert(name);
} }

View file

@ -61,7 +61,10 @@ impl Clone for FilesystemShell {
impl FilesystemShell { impl FilesystemShell {
pub fn basic(commands: CommandRegistry) -> Result<FilesystemShell, Error> { pub fn basic(commands: CommandRegistry) -> Result<FilesystemShell, Error> {
let path = std::env::current_dir()?; let path = match std::env::current_dir() {
Ok(path) => path,
Err(_) => PathBuf::from("/"),
};
Ok(FilesystemShell { Ok(FilesystemShell {
path: path.to_string_lossy().to_string(), path: path.to_string_lossy().to_string(),
@ -69,7 +72,7 @@ impl FilesystemShell {
completer: NuCompleter { completer: NuCompleter {
file_completer: FilenameCompleter::new(), file_completer: FilenameCompleter::new(),
commands, commands,
homedir: dirs::home_dir(), homedir: homedir_if_possible(),
}, },
hinter: HistoryHinter {}, hinter: HistoryHinter {},
}) })
@ -89,20 +92,32 @@ impl FilesystemShell {
completer: NuCompleter { completer: NuCompleter {
file_completer: FilenameCompleter::new(), file_completer: FilenameCompleter::new(),
commands, commands,
homedir: dirs::home_dir(), homedir: homedir_if_possible(),
}, },
hinter: HistoryHinter {}, hinter: HistoryHinter {},
}) })
} }
} }
pub fn homedir_if_possible() -> Option<PathBuf> {
#[cfg(feature = "dirs")]
{
dirs::home_dir()
}
#[cfg(not(feature = "dirs"))]
{
None
}
}
impl Shell for FilesystemShell { impl Shell for FilesystemShell {
fn name(&self) -> String { fn name(&self) -> String {
"filesystem".to_string() "filesystem".to_string()
} }
fn homedir(&self) -> Option<PathBuf> { fn homedir(&self) -> Option<PathBuf> {
dirs::home_dir() homedir_if_possible()
} }
fn ls( fn ls(
@ -195,7 +210,7 @@ impl Shell for FilesystemShell {
fn cd(&self, args: CdArgs, name: Tag) -> Result<OutputStream, ShellError> { fn cd(&self, args: CdArgs, name: Tag) -> Result<OutputStream, ShellError> {
let path = match args.path { let path = match args.path {
None => match dirs::home_dir() { None => match homedir_if_possible() {
Some(o) => o, Some(o) => o,
_ => { _ => {
return Err(ShellError::labeled_error( return Err(ShellError::labeled_error(

View file

@ -126,9 +126,17 @@ impl Shell for HelpShell {
} }
fn homedir(&self) -> Option<PathBuf> { fn homedir(&self) -> Option<PathBuf> {
#[cfg(feature = "dirs")]
{
dirs::home_dir() dirs::home_dir()
} }
#[cfg(not(feature = "dirs"))]
{
None
}
}
fn path(&self) -> String { fn path(&self) -> String {
self.path.clone() self.path.clone()
} }

View file

@ -126,9 +126,8 @@ impl InputStream {
} }
Some(Value { Some(Value {
tag: value_tag, tag: value_tag,
value: v, value: _,
}) => { }) => {
println!("{:?}", v);
return Err(ShellError::labeled_error_with_secondary( return Err(ShellError::labeled_error_with_secondary(
"Expected binary from pipeline", "Expected binary from pipeline",
"requires binary input", "requires binary input",

View file

@ -127,6 +127,7 @@ fn filesystem_change_current_directory_to_parent_directory_after_delete_cwd() {
}) })
} }
#[cfg(feature = "dirs")]
#[test] #[test]
fn filesystem_change_to_home_directory() { fn filesystem_change_to_home_directory() {
Playground::setup("cd_test_8", |dirs, _| { Playground::setup("cd_test_8", |dirs, _| {

View file

@ -54,6 +54,7 @@ fn parses_csv() {
// what appears to be an issue in the bson library that is under investigation. // what appears to be an issue in the bson library that is under investigation.
// //
#[cfg(feature = "bson")]
#[test] #[test]
fn parses_bson() { fn parses_bson() {
let actual = nu!( let actual = nu!(
@ -64,6 +65,7 @@ fn parses_bson() {
assert_eq!(actual.out, "hello"); assert_eq!(actual.out, "hello");
} }
#[cfg(feature = "bson")]
#[test] #[test]
fn parses_more_bson_complexity() { fn parses_more_bson_complexity() {
let actual = nu!( let actual = nu!(
@ -130,6 +132,7 @@ fn parses_more_bson_complexity() {
// 4 │ // 4 │
// ━━━┷━━━━━━ // ━━━┷━━━━━━
#[cfg(feature = "sqlite")]
#[test] #[test]
fn parses_sqlite() { fn parses_sqlite() {
let actual = nu!( let actual = nu!(

View file

@ -1,3 +1,4 @@
mod bool; mod bool;
mod dice; mod dice;
#[cfg(feature = "uuid_crate")]
mod uuid; mod uuid;

View file

@ -15,6 +15,7 @@ fn sets_the_column() {
assert_eq!(actual.out, "0.7.0"); assert_eq!(actual.out, "0.7.0");
} }
#[cfg(features = "inc")]
#[test] #[test]
fn sets_the_column_from_a_block_run_output() { fn sets_the_column_from_a_block_run_output() {
let actual = nu!( let actual = nu!(

View file

@ -1,4 +1,7 @@
use nu_test_support::{nu, pipeline}; use nu_test_support::nu;
#[cfg(feature = "sqlite")]
use nu_test_support::pipeline;
#[test] #[test]
fn filters_by_unit_size_comparison() { fn filters_by_unit_size_comparison() {
@ -40,6 +43,7 @@ fn where_not_in_table() {
assert_eq!(actual.out, "4"); assert_eq!(actual.out, "4");
} }
#[cfg(feature = "sqlite")]
#[test] #[test]
fn explicit_block_condition() { fn explicit_block_condition() {
let actual = nu!( let actual = nu!(
@ -58,6 +62,7 @@ fn explicit_block_condition() {
assert_eq!(actual.out, "4253"); assert_eq!(actual.out, "4253");
} }
#[cfg(feature = "sqlite")]
#[test] #[test]
fn binary_operator_comparisons() { fn binary_operator_comparisons() {
let actual = nu!( let actual = nu!(
@ -136,6 +141,7 @@ fn binary_operator_comparisons() {
assert_eq!(actual.out, "42"); assert_eq!(actual.out, "42");
} }
#[cfg(feature = "sqlite")]
#[test] #[test]
fn contains_operator() { fn contains_operator() {
let actual = nu!( let actual = nu!(

View file

@ -1,7 +1,7 @@
use nu_test_support::{nu, pipeline}; #[cfg(feature = "bson")]
#[test] #[test]
fn table_to_bson_and_back_into_table() { fn table_to_bson_and_back_into_table() {
use nu_test_support::{nu, pipeline};
let actual = nu!( let actual = nu!(
cwd: "tests/fixtures/formats", pipeline( cwd: "tests/fixtures/formats", pipeline(
r#" r#"

View file

@ -1,5 +1,7 @@
#[cfg(feature = "sqlite")]
use nu_test_support::{nu, pipeline}; use nu_test_support::{nu, pipeline};
#[cfg(feature = "sqlite")]
#[test] #[test]
fn table_to_sqlite_and_back_into_table() { fn table_to_sqlite_and_back_into_table() {
let actual = nu!( let actual = nu!(

View file

@ -95,6 +95,7 @@ impl Playground {
self self
} }
#[cfg(not(target_arch = "wasm32"))]
pub fn symlink(&mut self, from: impl AsRef<Path>, to: impl AsRef<Path>) -> &mut Self { pub fn symlink(&mut self, from: impl AsRef<Path>, to: impl AsRef<Path>) -> &mut Self {
let from = self.cwd.join(from); let from = self.cwd.join(from);
let to = self.cwd.join(to); let to = self.cwd.join(to);

View file

@ -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"}

View file

@ -0,0 +1,3 @@
fn main() -> Result<(), Box<dyn std::error::Error>> {
nu_build::build()
}

View file

@ -1,41 +1,22 @@
use crate::commands::WholeStreamCommand; use bigdecimal::BigDecimal;
use crate::prelude::*;
use bson::{decode_document, spec::BinarySubtype, Bson}; use bson::{decode_document, spec::BinarySubtype, Bson};
use nu_errors::{ExpectedRange, ShellError}; use nu_errors::{ExpectedRange, ShellError};
use nu_protocol::{Primitive, ReturnSuccess, Signature, TaggedDictBuilder, UntaggedValue, Value}; use nu_protocol::{Primitive, ReturnSuccess, ReturnValue, TaggedDictBuilder, UntaggedValue, Value};
use nu_source::SpannedItem; use nu_source::{SpannedItem, Tag};
use std::str::FromStr; use std::str::FromStr;
pub struct FromBSON; #[derive(Default)]
pub struct FromBSON {
pub state: Vec<u8>,
pub name_tag: Tag,
}
#[async_trait] impl FromBSON {
impl WholeStreamCommand for FromBSON { pub fn new() -> FromBSON {
fn name(&self) -> &str { FromBSON {
"from bson" state: vec![],
name_tag: Tag::unknown(),
} }
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<OutputStream, ShellError> {
from_bson(args, registry).await
}
fn examples(&self) -> Vec<Example> {
vec![Example {
description: "Convert bson data to a table",
example: "open file.bin | from bson",
result: None,
}]
} }
} }
@ -208,37 +189,13 @@ pub fn from_bson_bytes_to_value(bytes: Vec<u8>, tag: impl Into<Tag>) -> Result<V
convert_bson_value_to_nu_value(&Bson::Array(docs), tag) convert_bson_value_to_nu_value(&Bson::Array(docs), tag)
} }
async fn from_bson( pub fn from_bson(bytes: Vec<u8>, name_tag: Tag) -> Result<Vec<ReturnValue>, ShellError> {
args: CommandArgs, match from_bson_bytes_to_value(bytes, name_tag.clone()) {
registry: &CommandRegistry, Ok(x) => Ok(vec![ReturnSuccess::value(x)]),
) -> Result<OutputStream, ShellError> { Err(_) => Err(ShellError::labeled_error(
let registry = registry.clone();
let args = args.evaluate_once(&registry).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(
"Could not parse as BSON", "Could not parse as BSON",
"input cannot be parsed as BSON", "input cannot be parsed as BSON",
tag.clone(), name_tag,
"value originates from here",
bytes.tag,
)), )),
} }
} }
#[cfg(test)]
mod tests {
use super::FromBSON;
#[test]
fn examples_work_as_expected() {
use crate::examples::test as test_examples;
test_examples(FromBSON {})
}
}

View file

@ -0,0 +1,4 @@
mod from_bson;
mod nu;
pub use from_bson::FromBSON;

View file

@ -0,0 +1,6 @@
use nu_plugin::serve_plugin;
use nu_plugin_from_bson::FromBSON;
fn main() {
serve_plugin(&mut FromBSON::new())
}

View file

@ -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<Signature, ShellError> {
Ok(Signature::build("from bson")
.desc("Convert from .bson binary into table")
.filter())
}
fn begin_filter(&mut self, call_info: CallInfo) -> Result<Vec<ReturnValue>, ShellError> {
self.name_tag = call_info.name_tag;
Ok(vec![])
}
fn filter(&mut self, input: Value) -> Result<Vec<ReturnValue>, 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<Vec<ReturnValue>, ShellError> {
crate::from_bson::from_bson(self.state.clone(), Tag::unknown())
}
}

View file

@ -0,0 +1 @@
mod integration {}

View file

@ -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"}

View file

@ -0,0 +1,3 @@
fn main() -> Result<(), Box<dyn std::error::Error>> {
nu_build::build()
}

View file

@ -1,58 +1,22 @@
use crate::commands::WholeStreamCommand;
use crate::prelude::*;
use nu_errors::ShellError; 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 rusqlite::{types::ValueRef, Connection, Row, NO_PARAMS};
use std::io::Write; use std::io::Write;
use std::path::Path; use std::path::Path;
pub struct FromSQLite; #[derive(Default)]
pub struct FromSqlite {
#[async_trait] pub state: Vec<u8>,
impl WholeStreamCommand for FromSQLite { pub name_tag: Tag,
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<OutputStream, ShellError> {
from_sqlite(args, registry).await
}
} }
pub struct FromDB; impl FromSqlite {
pub fn new() -> FromSqlite {
#[async_trait] FromSqlite {
impl WholeStreamCommand for FromDB { state: vec![],
fn name(&self) -> &str { name_tag: Tag::unknown(),
"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<OutputStream, ShellError> {
from_sqlite(args, registry).await
} }
} }
@ -135,47 +99,19 @@ pub fn from_sqlite_bytes_to_value(
} }
} }
async fn from_sqlite( pub fn from_sqlite(bytes: Vec<u8>, name_tag: Tag) -> Result<Vec<ReturnValue>, ShellError> {
args: CommandArgs, match from_sqlite_bytes_to_value(bytes, name_tag.clone()) {
registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> {
let registry = registry.clone();
let args = args.evaluate_once(&registry).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()) {
Ok(x) => match x { Ok(x) => match x {
Value { Value {
value: UntaggedValue::Table(list), value: UntaggedValue::Table(list),
.. ..
} => Ok(futures::stream::iter(list).to_output_stream()), } => Ok(list.into_iter().map(ReturnSuccess::value).collect()),
_ => Ok(OutputStream::one(x)), _ => Ok(vec![ReturnSuccess::value(x)]),
}, },
Err(err) => { Err(_) => Err(ShellError::labeled_error(
println!("{:?}", err);
Err(ShellError::labeled_error_with_secondary(
"Could not parse as SQLite", "Could not parse as SQLite",
"input cannot be parsed as SQLite", "input cannot be parsed as SQLite",
&tag, &name_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 {})
} }
} }

View file

@ -0,0 +1,4 @@
mod from_sqlite;
mod nu;
pub use from_sqlite::FromSqlite;

View file

@ -0,0 +1,6 @@
use nu_plugin::serve_plugin;
use nu_plugin_from_sqlite::FromSqlite;
fn main() {
serve_plugin(&mut FromSqlite::new())
}

View file

@ -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<Signature, ShellError> {
Ok(Signature::build("from sqlite")
.desc("Convert from sqlite binary into table")
.filter())
}
fn begin_filter(&mut self, call_info: CallInfo) -> Result<Vec<ReturnValue>, ShellError> {
self.name_tag = call_info.name_tag;
Ok(vec![])
}
fn filter(&mut self, input: Value) -> Result<Vec<ReturnValue>, 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<Vec<ReturnValue>, ShellError> {
crate::from_sqlite::from_sqlite(self.state.clone(), Tag::unknown())
}
}

View file

@ -0,0 +1 @@
mod integration {}

View file

@ -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"}

View file

@ -0,0 +1,3 @@
fn main() -> Result<(), Box<dyn std::error::Error>> {
nu_build::build()
}

View file

@ -0,0 +1,4 @@
mod nu;
mod to_bson;
pub use to_bson::ToBSON;

View file

@ -0,0 +1,6 @@
use nu_plugin::serve_plugin;
use nu_plugin_to_bson::ToBSON;
fn main() {
serve_plugin(&mut ToBSON::new())
}

View file

@ -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<Signature, ShellError> {
Ok(Signature::build("to bson")
.desc("Convert table into .bson binary")
.filter())
}
fn filter(&mut self, input: Value) -> Result<Vec<ReturnValue>, ShellError> {
self.state.push(input);
Ok(vec![])
}
fn end_filter(&mut self) -> Result<Vec<ReturnValue>, ShellError> {
Ok(crate::to_bson::to_bson(self.state.clone(), Tag::unknown()))
}
}

View file

@ -0,0 +1 @@
mod integration {}

View file

@ -1,39 +1,21 @@
use crate::commands::WholeStreamCommand;
use crate::prelude::*;
use bson::{encode_document, oid::ObjectId, spec::BinarySubtype, Bson, Document}; use bson::{encode_document, oid::ObjectId, spec::BinarySubtype, Bson, Document};
use nu_errors::{CoerceInto, ShellError}; use nu_errors::{CoerceInto, ShellError};
use nu_protocol::{ use nu_protocol::{
Dictionary, Primitive, ReturnSuccess, Signature, SpannedTypeName, UnspannedPathMember, Dictionary, Primitive, ReturnSuccess, ReturnValue, SpannedTypeName, UnspannedPathMember,
UntaggedValue, Value, UntaggedValue, Value,
}; };
use nu_source::{Tag, TaggedItem};
use num_traits::ToPrimitive;
use std::convert::TryInto; use std::convert::TryInto;
pub struct ToBSON; #[derive(Default)]
pub struct ToBSON {
pub state: Vec<Value>,
}
#[async_trait] impl ToBSON {
impl WholeStreamCommand for ToBSON { pub fn new() -> ToBSON {
fn name(&self) -> &str { ToBSON { state: vec![] }
"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<OutputStream, ShellError> {
to_bson(args, registry).await
}
fn is_binary(&self) -> bool {
true
} }
} }
@ -261,17 +243,9 @@ fn bson_value_to_bytes(bson: Bson, tag: Tag) -> Result<Vec<u8>, ShellError> {
Ok(out) Ok(out)
} }
async fn to_bson( pub fn to_bson(input: Vec<Value>, name_tag: Tag) -> Vec<ReturnValue> {
args: CommandArgs,
registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> {
let registry = registry.clone();
let args = args.evaluate_once(&registry).await?;
let name_tag = args.name_tag();
let name_span = name_tag.span; let name_span = name_tag.span;
let input: Vec<Value> = args.input.collect().await;
let to_process_input = match input.len() { let to_process_input = match input.len() {
x if x > 1 => { x if x > 1 => {
let tag = input[0].tag.clone(); let tag = input[0].tag.clone();
@ -284,13 +258,14 @@ async fn to_bson(
_ => vec![], _ => vec![],
}; };
Ok(futures::stream::iter(to_process_input.into_iter().map( to_process_input
move |value| match value_to_bson_value(&value) { .into_iter()
.map(move |value| match value_to_bson_value(&value) {
Ok(bson_value) => { Ok(bson_value) => {
let value_span = value.tag.span; let value_span = value.tag.span;
match bson_value_to_bytes(bson_value, name_tag.clone()) { 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( _ => Err(ShellError::labeled_error_with_secondary(
"Expected a table with BSON-compatible structure from pipeline", "Expected a table with BSON-compatible structure from pipeline",
"requires BSON-compatible input", "requires BSON-compatible input",
@ -305,19 +280,6 @@ async fn to_bson(
"requires BSON-compatible input", "requires BSON-compatible input",
&name_tag, &name_tag,
)), )),
}, })
)) .collect()
.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 {})
}
} }

View file

@ -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"}

View file

@ -0,0 +1,3 @@
fn main() -> Result<(), Box<dyn std::error::Error>> {
nu_build::build()
}

View file

@ -0,0 +1,4 @@
mod nu;
mod to_sqlite;
pub use to_sqlite::ToSqlite;

View file

@ -0,0 +1,6 @@
use nu_plugin::serve_plugin;
use nu_plugin_to_sqlite::ToSqlite;
fn main() {
serve_plugin(&mut ToSqlite::new())
}

View file

@ -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<Signature, ShellError> {
Ok(Signature::build("to sqlite")
.desc("Convert table into sqlite binary")
.filter())
}
fn filter(&mut self, input: Value) -> Result<Vec<ReturnValue>, ShellError> {
self.state.push(input);
Ok(vec![])
}
fn end_filter(&mut self) -> Result<Vec<ReturnValue>, ShellError> {
crate::to_sqlite::to_sqlite(self.state.clone(), Tag::unknown())
}
}

View file

@ -0,0 +1 @@
mod integration {}

View file

@ -1,69 +1,20 @@
use crate::commands::WholeStreamCommand;
use crate::prelude::*;
use hex::encode; use hex::encode;
use nu_errors::ShellError; 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 rusqlite::{Connection, NO_PARAMS};
use std::io::Read; use std::io::Read;
pub struct ToSQLite; #[derive(Default)]
pub struct ToSqlite {
#[async_trait] pub state: Vec<Value>,
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<OutputStream, ShellError> {
to_sqlite(args, registry).await
}
fn is_binary(&self) -> bool {
true
}
} }
pub struct ToDB; impl ToSqlite {
pub fn new() -> ToSqlite {
#[async_trait] ToSqlite { state: vec![] }
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<OutputStream, ShellError> {
to_sqlite(args, registry).await
}
fn is_binary(&self) -> bool {
true
} }
} }
fn comma_concat(acc: String, current: String) -> String { fn comma_concat(acc: String, current: String) -> String {
if acc == "" { if acc == "" {
current current
@ -183,9 +134,6 @@ fn sqlite_input_stream_to_bytes(values: Vec<Value>) -> Result<Value, std::io::Er
{ {
Ok(_) => (), Ok(_) => (),
Err(e) => { Err(e) => {
outln!("{}", create);
outln!("{}", insert);
outln!("{:?}", e);
return Err(std::io::Error::new(std::io::ErrorKind::Other, e)); return Err(std::io::Error::new(std::io::ErrorKind::Other, e));
} }
} }
@ -203,17 +151,9 @@ fn sqlite_input_stream_to_bytes(values: Vec<Value>) -> Result<Value, std::io::Er
Ok(UntaggedValue::binary(out).into_value(tag)) Ok(UntaggedValue::binary(out).into_value(tag))
} }
async fn to_sqlite( pub fn to_sqlite(input: Vec<Value>, name_tag: Tag) -> Result<Vec<ReturnValue>, ShellError> {
args: CommandArgs,
registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> {
let registry = registry.clone();
let args = args.evaluate_once(&registry).await?;
let name_tag = args.name_tag();
let input: Vec<Value> = args.input.collect().await;
match sqlite_input_stream_to_bytes(input) { 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( _ => Err(ShellError::labeled_error(
"Expected a table with SQLite-compatible structure from pipeline", "Expected a table with SQLite-compatible structure from pipeline",
"requires SQLite-compatible input", "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 {})
}
}

View file

@ -1 +1,2 @@
#[cfg(features = "inc")]
mod core_inc; mod core_inc;

View file

@ -1,5 +1,6 @@
use nu_test_support::nu; use nu_test_support::nu;
#[cfg(feature = "which")]
#[test] #[test]
fn shows_error_for_command_not_found() { fn shows_error_for_command_not_found() {
let actual = nu!( let actual = nu!(
@ -10,6 +11,7 @@ fn shows_error_for_command_not_found() {
assert!(actual.err.contains("Command not found")); assert!(actual.err.contains("Command not found"));
} }
#[cfg(feature = "which")]
#[test] #[test]
fn shows_error_for_command_not_found_in_pipeline() { fn shows_error_for_command_not_found_in_pipeline() {
let actual = nu!( let actual = nu!(
@ -20,6 +22,7 @@ fn shows_error_for_command_not_found_in_pipeline() {
assert!(actual.err.contains("Command not found")); assert!(actual.err.contains("Command not found"));
} }
#[cfg(feature = "which")]
#[test] #[test]
fn automatically_change_directory() { fn automatically_change_directory() {
use nu_test_support::playground::Playground; use nu_test_support::playground::Playground;

View file

@ -1,4 +1,5 @@
use nu_test_support::fs::Stub::EmptyFile; use nu_test_support::fs::Stub::EmptyFile;
#[cfg(feature = "which")]
use nu_test_support::fs::Stub::FileWithContent; use nu_test_support::fs::Stub::FileWithContent;
use nu_test_support::fs::Stub::FileWithContentToBeTrimmed; use nu_test_support::fs::Stub::FileWithContentToBeTrimmed;
use nu_test_support::nu; 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] #[test]
fn autoenv() { fn autoenv() {
Playground::setup("autoenv_test", |dirs, sandbox| { Playground::setup("autoenv_test", |dirs, sandbox| {
@ -379,6 +381,7 @@ fn string_interpolation_with_it_column_path() {
assert_eq!(actual.out, "sammie"); assert_eq!(actual.out, "sammie");
} }
#[cfg(feature = "which")]
#[test] #[test]
fn argument_invocation_reports_errors() { fn argument_invocation_reports_errors() {
let actual = nu!( let actual = nu!(