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:
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

66
Cargo.lock generated
View file

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

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_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

View file

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

View file

@ -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<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
pub async fn process_line(
readline: Result<String, ReadlineError>,
@ -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)]

View file

@ -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;

View file

@ -26,7 +26,7 @@ pub(crate) async fn run_external_command(
) -> Result<InputStream, ShellError> {
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<std::path::PathBuf> {
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<Value> {
@ -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())

View file

@ -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;

View file

@ -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 {

View file

@ -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<OutputSt
}
}
if let Ok(paths) = ichwh::which_all(&item).await {
for path in paths {
output.push(ReturnSuccess::value(entry_path!(
item,
path.into(),
application.tag.clone()
)));
#[cfg(feature = "ichwh")]
{
if let Ok(paths) = ichwh::which_all(&item).await {
for path in paths {
output.push(ReturnSuccess::value(entry_path!(
item,
path.into(),
application.tag.clone()
)));
}
}
}

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();
#[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::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<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> {
@ -39,21 +55,30 @@ pub fn default_path_for(file: &Option<PathBuf>) -> Result<PathBuf, ShellError> {
Ok(filename)
}
#[cfg(feature = "directories")]
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")
.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<PathBuf, ShellError> {
// FIXME: unsure if this should be error or a simple default
Ok(std::path::PathBuf::from("/"))
}
pub fn read(
tag: impl Into<Tag>,
at: &Option<PathBuf>,

View file

@ -6,9 +6,6 @@ use std::ffi::OsString;
use std::fmt::Debug;
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 err_termcolor(&self) -> termcolor::StandardStream;
@ -24,14 +21,6 @@ pub trait Host: Debug + Send {
}
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) {
(**self).stdout(out)
}
@ -73,14 +62,6 @@ impl Host for Box<dyn Host> {
pub struct 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) {
match out {
"\n" => outln!(""),
@ -96,19 +77,40 @@ impl Host for BasicHost {
}
fn vars(&mut self) -> Vec<(String, String)> {
std::env::vars().collect::<Vec<_>>()
#[cfg(not(target_arch = "wasm32"))]
{
std::env::vars().collect::<Vec<_>>()
}
#[cfg(target_arch = "wasm32")]
{
vec![]
}
}
fn env_get(&mut self, key: OsString) -> Option<OsString> {
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<Box<term::StdoutTerminal>> {
None
}
fn err_terminal(&self) -> Option<Box<term::StderrTerminal>> {
None
}
fn stdout(&mut self, out: &str) {
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()?;
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));
}

View file

@ -1,6 +1,4 @@
use crate::prelude::*;
use git2::{Repository, RepositoryOpenFlags};
use std::ffi::OsString;
pub fn current_branch() -> Option<String> {
if let Ok(config) = crate::data::config::config(Tag::unknown()) {
@ -13,19 +11,29 @@ pub fn current_branch() -> Option<String> {
};
if !use_starship {
let v: Vec<OsString> = 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<OsString> = 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

View file

@ -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;

View file

@ -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<bool> {
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<bool> {
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<IndexSet<String>> {
@ -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);
}

View file

@ -61,7 +61,10 @@ impl Clone for FilesystemShell {
impl FilesystemShell {
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 {
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<PathBuf> {
#[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<PathBuf> {
dirs::home_dir()
homedir_if_possible()
}
fn ls(
@ -195,7 +210,7 @@ impl Shell for FilesystemShell {
fn cd(&self, args: CdArgs, name: Tag) -> Result<OutputStream, ShellError> {
let path = match args.path {
None => match dirs::home_dir() {
None => match homedir_if_possible() {
Some(o) => o,
_ => {
return Err(ShellError::labeled_error(

View file

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

View file

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

View file

@ -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, _| {

View file

@ -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!(

View file

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

View file

@ -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!(

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]
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!(

View file

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

View file

@ -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!(

View file

@ -95,6 +95,7 @@ impl Playground {
self
}
#[cfg(not(target_arch = "wasm32"))]
pub fn symlink(&mut self, from: impl AsRef<Path>, to: impl AsRef<Path>) -> &mut Self {
let from = self.cwd.join(from);
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 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<u8>,
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<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,
}]
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<u8>, tag: impl Into<Tag>) -> Result<V
convert_bson_value_to_nu_value(&Bson::Array(docs), tag)
}
async fn from_bson(
args: CommandArgs,
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_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<u8>, name_tag: Tag) -> Result<Vec<ReturnValue>, 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 {})
}
}

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_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<OutputStream, ShellError> {
from_sqlite(args, registry).await
}
#[derive(Default)]
pub struct FromSqlite {
pub state: Vec<u8>,
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<OutputStream, ShellError> {
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<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()) {
pub fn from_sqlite(bytes: Vec<u8>, name_tag: Tag) -> Result<Vec<ReturnValue>, 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,
)),
}
}

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 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<Value>,
}
#[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<OutputStream, ShellError> {
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<Vec<u8>, ShellError> {
Ok(out)
}
async fn to_bson(
args: CommandArgs,
registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> {
let registry = registry.clone();
let args = args.evaluate_once(&registry).await?;
let name_tag = args.name_tag();
pub fn to_bson(input: Vec<Value>, name_tag: Tag) -> Vec<ReturnValue> {
let name_span = name_tag.span;
let input: Vec<Value> = 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()
}

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 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<OutputStream, ShellError> {
to_sqlite(args, registry).await
}
fn is_binary(&self) -> bool {
true
}
#[derive(Default)]
pub struct ToSqlite {
pub state: Vec<Value>,
}
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<OutputStream, ShellError> {
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<Value>) -> Result<Value, std::io::Er
{
Ok(_) => (),
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<Value>) -> Result<Value, std::io::Er
Ok(UntaggedValue::binary(out).into_value(tag))
}
async fn to_sqlite(
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;
pub fn to_sqlite(input: Vec<Value>, name_tag: Tag) -> Result<Vec<ReturnValue>, 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 {})
}
}

View file

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

View file

@ -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;

View file

@ -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!(