Merge master

This commit is contained in:
Jonathan Turner
2019-09-13 06:33:52 +12:00
40 changed files with 365 additions and 564 deletions

View File

@ -15,7 +15,6 @@ use crate::parser::{hir, CallNode, Pipeline, PipelineElement, TokenNode};
use crate::prelude::*;
use log::{debug, trace};
use regex::Regex;
use rustyline::error::ReadlineError;
use rustyline::{self, config::Configurer, config::EditMode, ColorMode, Config, Editor};
use std::env;
@ -98,38 +97,12 @@ fn load_plugin(path: &std::path::Path, context: &mut Context) -> Result<(), Shel
result
}
fn load_plugins_in_dir(path: &std::path::PathBuf, context: &mut Context) -> Result<(), ShellError> {
let re_bin = Regex::new(r"^nu_plugin_[A-Za-z_]+$")?;
let re_exe = Regex::new(r"^nu_plugin_[A-Za-z_]+\.(exe|bat)$")?;
fn search_paths() -> Vec<std::path::PathBuf> {
let mut search_paths = Vec::new();
trace!("Looking for plugins in {:?}", path);
match std::fs::read_dir(path) {
Ok(p) => {
for entry in p {
let entry = entry?;
let filename = entry.file_name();
let f_name = filename.to_string_lossy();
if re_bin.is_match(&f_name) || re_exe.is_match(&f_name) {
let mut load_path = path.clone();
trace!("Found {:?}", f_name);
load_path.push(f_name.to_string());
load_plugin(&load_path, context)?;
}
}
}
_ => {}
}
Ok(())
}
fn load_plugins(context: &mut Context) -> Result<(), ShellError> {
match env::var_os("PATH") {
Some(paths) => {
for path in env::split_paths(&paths) {
let _ = load_plugins_in_dir(&path, context);
}
search_paths = env::split_paths(&paths).collect::<Vec<_>>();
}
None => println!("PATH is not defined in the environment."),
}
@ -140,7 +113,7 @@ fn load_plugins(context: &mut Context) -> Result<(), ShellError> {
let mut path = std::path::PathBuf::from(".");
path.push("target");
path.push("debug");
let _ = load_plugins_in_dir(&path, context);
search_paths.push(path);
}
#[cfg(not(debug_assertions))]
@ -149,8 +122,78 @@ fn load_plugins(context: &mut Context) -> Result<(), ShellError> {
let mut path = std::path::PathBuf::from(".");
path.push("target");
path.push("release");
search_paths.push(path);
}
let _ = load_plugins_in_dir(&path, context);
search_paths
}
fn load_plugins(context: &mut Context) -> Result<(), ShellError> {
let opts = glob::MatchOptions {
case_sensitive: false,
require_literal_separator: false,
require_literal_leading_dot: false,
};
for path in search_paths() {
let mut pattern = path.to_path_buf();
pattern.push(std::path::Path::new("nu_plugin_[a-z]*"));
match glob::glob_with(&pattern.to_string_lossy(), opts) {
Err(_) => {}
Ok(binaries) => {
for bin in binaries.filter_map(Result::ok) {
if !bin.is_file() {
continue;
}
let bin_name = {
if let Some(name) = bin.file_name() {
match name.to_str() {
Some(raw) => raw,
None => continue,
}
} else {
continue;
}
};
let is_valid_name = {
#[cfg(windows)]
{
bin_name
.chars()
.all(|c| c.is_ascii_alphabetic() || c == '_' || c == '.')
}
#[cfg(not(windows))]
{
bin_name
.chars()
.all(|c| c.is_ascii_alphabetic() || c == '_')
}
};
let is_executable = {
#[cfg(windows)]
{
bin_name.ends_with(".exe") || bin_name.ends_with(".bat")
}
#[cfg(not(windows))]
{
true
}
};
if is_valid_name && is_executable {
trace!("Trying {:?}", bin.display());
load_plugin(&bin, context)?;
}
}
}
}
}
Ok(())

View File

@ -10,8 +10,7 @@ impl WholeStreamCommand for CD {
}
fn signature(&self) -> Signature {
Signature::build("cd")
.optional("directory", SyntaxType::Path)
Signature::build("cd").optional("directory", SyntaxType::Path)
}
fn usage(&self) -> &str {

View File

@ -1,5 +1,5 @@
use crate::errors::ShellError;
use crate::data::{Dictionary, Value};
use crate::errors::ShellError;
use crate::prelude::*;
use chrono::{DateTime, Local, Utc};

View File

@ -11,8 +11,7 @@ impl WholeStreamCommand for Exit {
}
fn signature(&self) -> Signature {
Signature::build("exit")
.switch("now")
Signature::build("exit").switch("now")
}
fn usage(&self) -> &str {

View File

@ -16,8 +16,7 @@ impl WholeStreamCommand for First {
}
fn signature(&self) -> Signature {
Signature::build("first")
.required("amount", SyntaxType::Literal)
Signature::build("first").required("amount", SyntaxType::Literal)
}
fn usage(&self) -> &str {

View File

@ -1,12 +1,13 @@
use crate::commands::WholeStreamCommand;
use crate::errors::ShellError;
use crate::data::Value;
use crate::errors::ShellError;
use crate::prelude::*;
pub struct Get;
#[derive(Deserialize)]
pub struct GetArgs {
member: Tagged<String>,
rest: Vec<Tagged<String>>,
}
@ -16,7 +17,9 @@ impl WholeStreamCommand for Get {
}
fn signature(&self) -> Signature {
Signature::build("get").rest(SyntaxType::Member)
Signature::build("get")
.required("member", SyntaxType::Member)
.rest(SyntaxType::Member)
}
fn usage(&self) -> &str {
@ -63,13 +66,24 @@ fn get_member(path: &Tagged<String>, obj: &Tagged<Value>) -> Result<Tagged<Value
}
pub fn get(
GetArgs { rest: fields }: GetArgs,
GetArgs {
member,
rest: fields,
}: GetArgs,
RunnableContext { input, .. }: RunnableContext,
) -> Result<OutputStream, ShellError> {
let stream = input
.values
.map(move |item| {
let mut result = VecDeque::new();
let member = vec![member.clone()];
let fields = vec![&member, &fields]
.into_iter()
.flatten()
.collect::<Vec<&Tagged<String>>>();
for field in &fields {
match get_member(field, &item) {
Ok(Tagged {

View File

@ -101,20 +101,6 @@ impl PerItemCommand for Help {
}
}
// pub struct Signature {
// pub name: String,
// #[new(default)]
// pub usage: String,
// #[new(default)]
// pub positional: Vec<PositionalType>,
// #[new(value = "None")]
// pub rest_positional: Option<SyntaxType>,
// #[new(default)]
// pub named: IndexMap<String, NamedType>,
// #[new(value = "false")]
// pub is_filter: bool,
// }
help.push_back(ReturnSuccess::value(
Value::string(long_desc).tagged(tag.clone()),
));
@ -125,9 +111,11 @@ impl PerItemCommand for Help {
}
_ => {
let msg = r#"Welcome to Nushell.
Here are some tips to help you get started.
* help commands - list all available commands
* help <command name> - display help about a particular command
* help commands - list all available commands
* help <command name> - display help about a particular command
You can also learn more at http://book.nushell.sh"#;
let mut output_stream = VecDeque::new();

View File

@ -1,6 +1,6 @@
use crate::commands::WholeStreamCommand;
use crate::errors::ShellError;
use crate::data::{Primitive, Value};
use crate::errors::ShellError;
use crate::prelude::*;
use log::trace;

View File

@ -1,7 +1,7 @@
use crate::commands::WholeStreamCommand;
use crate::context::CommandRegistry;
use crate::errors::ShellError;
use crate::data::base::select_fields;
use crate::errors::ShellError;
use crate::prelude::*;
#[derive(Deserialize)]

View File

@ -1,6 +1,6 @@
use crate::commands::WholeStreamCommand;
use crate::errors::ShellError;
use crate::data::base::reject_fields;
use crate::errors::ShellError;
use crate::prelude::*;
#[derive(Deserialize)]

View File

@ -1,6 +1,6 @@
use crate::commands::WholeStreamCommand;
use crate::errors::ShellError;
use crate::data::TaggedDictBuilder;
use crate::errors::ShellError;
use crate::prelude::*;
pub struct Shells;

View File

@ -1,6 +1,6 @@
use crate::commands::WholeStreamCommand;
use crate::errors::ShellError;
use crate::data::{TaggedDictBuilder, Value};
use crate::errors::ShellError;
use crate::prelude::*;
pub struct Size;

View File

@ -1,6 +1,6 @@
use crate::commands::WholeStreamCommand;
use crate::errors::ShellError;
use crate::data::{Primitive, TaggedDictBuilder, Value};
use crate::errors::ShellError;
use crate::prelude::*;
use log::trace;
@ -40,7 +40,11 @@ impl WholeStreamCommand for SplitColumn {
}
fn split_column(
SplitColumnArgs { separator, rest, collapse_empty}: SplitColumnArgs,
SplitColumnArgs {
separator,
rest,
collapse_empty,
}: SplitColumnArgs,
RunnableContext { input, name, .. }: RunnableContext,
) -> Result<OutputStream, ShellError> {
Ok(input

View File

@ -1,6 +1,6 @@
use crate::commands::WholeStreamCommand;
use crate::errors::ShellError;
use crate::data::{Primitive, Value};
use crate::errors::ShellError;
use crate::prelude::*;
use log::trace;
@ -17,8 +17,7 @@ impl WholeStreamCommand for SplitRow {
}
fn signature(&self) -> Signature {
Signature::build("split-row")
.required("separator", SyntaxType::Any)
Signature::build("split-row").required("separator", SyntaxType::Any)
}
fn usage(&self) -> &str {

View File

@ -1,6 +1,6 @@
use crate::commands::WholeStreamCommand;
use crate::errors::ShellError;
use crate::data::{TaggedDictBuilder, Value};
use crate::errors::ShellError;
use crate::prelude::*;
pub struct Tags;

View File

@ -1,6 +1,6 @@
use crate::commands::WholeStreamCommand;
use crate::errors::ShellError;
use crate::data::Value;
use crate::errors::ShellError;
use crate::prelude::*;
pub struct Trim;

View File

@ -1,6 +1,6 @@
use crate::commands::WholeStreamCommand;
use crate::errors::ShellError;
use crate::data::{Dictionary, Value};
use crate::errors::ShellError;
use crate::parser::registry::Signature;
use crate::prelude::*;
use indexmap::IndexMap;

View File

@ -12,8 +12,7 @@ impl PerItemCommand for Where {
}
fn signature(&self) -> registry::Signature {
Signature::build("where")
.required("condition", SyntaxType::Block)
Signature::build("where").required("condition", SyntaxType::Block)
}
fn usage(&self) -> &str {
@ -43,9 +42,7 @@ impl PerItemCommand for Where {
VecDeque::new()
}
}
Err(e) => {
return Err(e)
}
Err(e) => return Err(e),
}
}
Tagged { tag, .. } => {

View File

@ -1,5 +1,5 @@
use crate::errors::ShellError;
use crate::data::Value;
use crate::errors::ShellError;
use crate::prelude::*;
use crate::commands::WholeStreamCommand;
@ -13,8 +13,7 @@ impl WholeStreamCommand for Which {
}
fn signature(&self) -> Signature {
Signature::build("which")
.required("name", SyntaxType::Any)
Signature::build("which").required("name", SyntaxType::Any)
}
fn usage(&self) -> &str {

View File

@ -555,16 +555,6 @@ impl std::convert::From<serde_json::Error> for ShellError {
}
}
impl std::convert::From<regex::Error> for ShellError {
fn from(input: regex::Error) -> ShellError {
ProximateShellError::String(StringError {
title: format!("{:?}", input),
error: Value::nothing(),
})
.start()
}
}
impl std::convert::From<Box<dyn std::error::Error + Send + Sync>> for ShellError {
fn from(input: Box<dyn std::error::Error + Send + Sync>) -> ShellError {
ProximateShellError::String(StringError {

View File

@ -28,8 +28,8 @@ pub use crate::parser::parse::token_tree_builder::TokenTreeBuilder;
pub use crate::plugin::{serve_plugin, Plugin};
pub use crate::utils::{AbsoluteFile, AbsolutePath, RelativePath};
pub use cli::cli;
pub use data::config::{APP_INFO, config_path};
pub use data::base::{Primitive, Value};
pub use data::config::{config_path, APP_INFO};
pub use data::dict::{Dictionary, TaggedDictBuilder};
pub use data::meta::{Span, Tag, Tagged, TaggedItem};
pub use errors::{CoerceInto, ShellError};

View File

@ -20,7 +20,6 @@ impl ToDebug for Operator {
}
impl Operator {
pub fn print(&self) -> String {
self.as_str().to_string()
}

View File

@ -15,7 +15,7 @@ impl Skip {
impl Plugin for Skip {
fn config(&mut self) -> Result<Signature, ShellError> {
Ok(Signature::build("skip")
Ok(Signature::build("skip")
.desc("Skip a number of rows")
.rest(SyntaxType::Number)
.filter())

View File

@ -2,20 +2,12 @@ use nu::{
serve_plugin, CallInfo, Plugin, Primitive, ReturnSuccess, ReturnValue, ShellError, Signature,
SyntaxType, Tagged, Value,
};
use regex::Regex;
#[derive(Debug, Eq, PartialEq)]
enum Action {
Downcase,
Upcase,
ToInteger,
Replace(ReplaceAction),
}
#[derive(Debug, Eq, PartialEq)]
enum ReplaceAction {
Direct,
FindAndReplace,
}
struct Str {
@ -45,41 +37,12 @@ impl Str {
Err(_) => Value::string(input),
},
},
Some(Action::Replace(ref mode)) => match mode {
ReplaceAction::Direct => Value::string(self.first_param()),
ReplaceAction::FindAndReplace => {
let regex = Regex::new(self.first_param());
match regex {
Ok(re) => Value::string(re.replace(input, self.second_param()).to_owned()),
Err(_) => Value::string(input),
}
}
},
None => Value::string(input),
};
Ok(applied)
}
fn did_supply_field(&self) -> bool {
self.field.is_some()
}
fn first_param(&self) -> &str {
let idx = if self.did_supply_field() { 1 } else { 0 };
self.get_param(idx)
}
fn second_param(&self) -> &str {
let idx = if self.did_supply_field() { 2 } else { 1 };
self.get_param(idx)
}
fn get_param(&self, idx: usize) -> &str {
self.params.as_ref().unwrap().get(idx).unwrap().as_str()
}
fn for_field(&mut self, field: &str) {
self.field = Some(String::from(field));
}
@ -92,14 +55,6 @@ impl Str {
self.error = Some(message.to_string());
}
fn for_replace(&mut self, mode: ReplaceAction) {
if self.permit() {
self.action = Some(Action::Replace(mode));
} else {
self.log_error("can only apply one");
}
}
fn for_to_int(&mut self) {
if self.permit() {
self.action = Some(Action::ToInteger);
@ -125,7 +80,7 @@ impl Str {
}
pub fn usage() -> &'static str {
"Usage: str field [--downcase|--upcase|--to-int|--replace|--find-replace]"
"Usage: str field [--downcase|--upcase|--to-int]"
}
}
@ -172,8 +127,6 @@ impl Plugin for Str {
.switch("downcase")
.switch("upcase")
.switch("to-int")
.switch("replace")
.switch("find-replace")
.rest(SyntaxType::Member)
.filter())
}
@ -190,12 +143,6 @@ impl Plugin for Str {
if args.has("to-int") {
self.for_to_int();
}
if args.has("replace") {
self.for_replace(ReplaceAction::Direct);
}
if args.has("find-replace") {
self.for_replace(ReplaceAction::FindAndReplace);
}
if let Some(possible_field) = args.nth(0) {
match possible_field {
@ -203,16 +150,6 @@ impl Plugin for Str {
item: Value::Primitive(Primitive::String(s)),
..
} => match self.action {
Some(Action::Replace(ReplaceAction::Direct)) => {
if args.len() == 2 {
self.for_field(&s);
}
}
Some(Action::Replace(ReplaceAction::FindAndReplace)) => {
if args.len() == 3 {
self.for_field(&s);
}
}
Some(Action::Downcase)
| Some(Action::Upcase)
| Some(Action::ToInteger)
@ -258,7 +195,7 @@ fn main() {
#[cfg(test)]
mod tests {
use super::{Action, ReplaceAction, Str};
use super::{Action, Str};
use indexmap::IndexMap;
use nu::{
CallInfo, EvaluatedArgs, Plugin, Primitive, ReturnSuccess, SourceMap, Span, Tag, Tagged,
@ -266,16 +203,6 @@ mod tests {
};
use num_bigint::BigInt;
impl Str {
fn replace_with(&mut self, value: &str) {
self.params.as_mut().unwrap().push(value.to_string());
}
fn find_with(&mut self, search: &str) {
self.params.as_mut().unwrap().push(search.to_string());
}
}
struct CallStub {
positionals: Vec<Tagged<Value>>,
flags: IndexMap<String, Tagged<Value>>,
@ -328,7 +255,7 @@ mod tests {
let configured = plugin.config().unwrap();
for action_flag in &["downcase", "upcase", "to-int", "replace", "find-replace"] {
for action_flag in &["downcase", "upcase", "to-int"] {
assert!(configured.named.get(*action_flag).is_some());
}
}
@ -362,33 +289,6 @@ mod tests {
.is_ok());
assert_eq!(plugin.action.unwrap(), Action::ToInteger);
}
#[test]
fn str_plugin_accepts_replace() {
let mut plugin = Str::new();
assert!(plugin
.begin_filter(CallStub::new().with_long_flag("replace").create())
.is_ok());
assert_eq!(
plugin.action.unwrap(),
Action::Replace(ReplaceAction::Direct)
);
}
#[test]
fn str_plugin_accepts_find_replace() {
let mut plugin = Str::new();
assert!(plugin
.begin_filter(CallStub::new().with_long_flag("find-replace").create())
.is_ok());
assert_eq!(
plugin.action.unwrap(),
Action::Replace(ReplaceAction::FindAndReplace)
);
}
#[test]
fn str_plugin_accepts_field() {
let mut plugin = Str::new();
@ -441,26 +341,6 @@ mod tests {
assert_eq!(strutils.apply("9999").unwrap(), Value::int(9999 as i64));
}
#[test]
fn str_replace() {
let mut strutils = Str::new();
strutils.for_replace(ReplaceAction::Direct);
strutils.replace_with("robalino");
assert_eq!(strutils.apply("andres").unwrap(), Value::string("robalino"));
}
#[test]
fn str_find_replace() {
let mut strutils = Str::new();
strutils.for_replace(ReplaceAction::FindAndReplace);
strutils.find_with(r"kittens");
strutils.replace_with("jotandrehuda");
assert_eq!(
strutils.apply("wykittens").unwrap(),
Value::string("wyjotandrehuda")
);
}
#[test]
fn str_plugin_applies_upcase_with_field() {
let mut plugin = Str::new();
@ -604,114 +484,4 @@ mod tests {
_ => {}
}
}
#[test]
fn str_plugin_applies_replace_with_field() {
let mut plugin = Str::new();
assert!(plugin
.begin_filter(
CallStub::new()
.with_parameter("rustconf")
.with_parameter("22nd August 2019")
.with_long_flag("replace")
.create()
)
.is_ok());
let subject = structured_sample_record("rustconf", "1st January 1970");
let output = plugin.filter(subject).unwrap();
match output[0].as_ref().unwrap() {
ReturnSuccess::Value(Tagged {
item: Value::Row(o),
..
}) => assert_eq!(
*o.get_data(&String::from("rustconf")).borrow(),
Value::string(String::from("22nd August 2019"))
),
_ => {}
}
}
#[test]
fn str_plugin_applies_replace_without_field() {
let mut plugin = Str::new();
assert!(plugin
.begin_filter(
CallStub::new()
.with_parameter("22nd August 2019")
.with_long_flag("replace")
.create()
)
.is_ok());
let subject = unstructured_sample_record("1st January 1970");
let output = plugin.filter(subject).unwrap();
match output[0].as_ref().unwrap() {
ReturnSuccess::Value(Tagged {
item: Value::Primitive(Primitive::String(s)),
..
}) => assert_eq!(*s, String::from("22nd August 2019")),
_ => {}
}
}
#[test]
fn str_plugin_applies_find_replace_with_field() {
let mut plugin = Str::new();
assert!(plugin
.begin_filter(
CallStub::new()
.with_parameter("staff")
.with_parameter("kittens")
.with_parameter("jotandrehuda")
.with_long_flag("find-replace")
.create()
)
.is_ok());
let subject = structured_sample_record("staff", "wykittens");
let output = plugin.filter(subject).unwrap();
match output[0].as_ref().unwrap() {
ReturnSuccess::Value(Tagged {
item: Value::Row(o),
..
}) => assert_eq!(
*o.get_data(&String::from("staff")).borrow(),
Value::string(String::from("wyjotandrehuda"))
),
_ => {}
}
}
#[test]
fn str_plugin_applies_find_replace_without_field() {
let mut plugin = Str::new();
assert!(plugin
.begin_filter(
CallStub::new()
.with_parameter("kittens")
.with_parameter("jotandrehuda")
.with_long_flag("find-replace")
.create()
)
.is_ok());
let subject = unstructured_sample_record("wykittens");
let output = plugin.filter(subject).unwrap();
match output[0].as_ref().unwrap() {
ReturnSuccess::Value(Tagged {
item: Value::Primitive(Primitive::String(s)),
..
}) => assert_eq!(*s, String::from("wyjotandrehuda")),
_ => {}
}
}
}

View File

@ -1,9 +1,9 @@
pub(crate) mod completer;
pub(crate) mod filesystem_shell;
pub(crate) mod help_shell;
pub(crate) mod helper;
pub(crate) mod shell;
pub(crate) mod shell_manager;
pub(crate) mod value_shell;
pub(crate) mod help_shell;
pub(crate) use helper::Helper;

View File

@ -207,8 +207,7 @@ impl Shell for FilesystemShell {
let mut stream = VecDeque::new();
stream.push_back(
ReturnSuccess::change_cwd(
stream.push_back(ReturnSuccess::change_cwd(
path.to_string_lossy().to_string(),
));