diff --git a/crates/nu-command/src/commands.rs b/crates/nu-command/src/commands.rs index 00ea99b355..2331ad81c5 100644 --- a/crates/nu-command/src/commands.rs +++ b/crates/nu-command/src/commands.rs @@ -299,6 +299,34 @@ mod tests { whole_stream_command(Move), whole_stream_command(Update), whole_stream_command(Empty), + // Str Command Suite + whole_stream_command(Str), + whole_stream_command(StrToDecimal), + whole_stream_command(StrToInteger), + whole_stream_command(StrDowncase), + whole_stream_command(StrUpcase), + whole_stream_command(StrCapitalize), + whole_stream_command(StrFindReplace), + // whole_stream_command(StrFrom), + whole_stream_command(StrSubstring), + whole_stream_command(StrToDatetime), + whole_stream_command(StrContains), + whole_stream_command(StrIndexOf), + whole_stream_command(StrTrim), + whole_stream_command(StrTrimLeft), + whole_stream_command(StrTrimRight), + whole_stream_command(StrStartsWith), + whole_stream_command(StrEndsWith), + //whole_stream_command(StrCollect), + //whole_stream_command(StrLength), + whole_stream_command(StrLPad), + //whole_stream_command(StrReverse), + whole_stream_command(StrRPad), + whole_stream_command(StrCamelCase), + whole_stream_command(StrPascalCase), + whole_stream_command(StrKebabCase), + whole_stream_command(StrSnakeCase), + whole_stream_command(StrScreamingSnakeCase), ] } diff --git a/crates/nu-command/src/examples.rs b/crates/nu-command/src/examples.rs index 524957e406..b691b9a35b 100644 --- a/crates/nu-command/src/examples.rs +++ b/crates/nu-command/src/examples.rs @@ -1,27 +1,27 @@ +mod sample; + +mod double_echo; +mod double_ls; +mod stub_generate; + +use double_echo::Command as DoubleEcho; +use double_ls::Command as DoubleLs; +use stub_generate::{mock_path, Command as StubOpen}; + use nu_engine::basic_evaluation_context; use nu_errors::ShellError; use nu_parser::ParserScope; use nu_protocol::hir::ClassifiedBlock; -use nu_protocol::{ - Primitive, ReturnSuccess, ShellTypeName, Signature, SyntaxShape, UntaggedValue, Value, -}; -use nu_source::{AnchorLocation, TaggedItem}; - -use crate::prelude::*; - -use num_bigint::BigInt; +use nu_protocol::{ShellTypeName, Value}; +use nu_source::AnchorLocation; use crate::commands::{ BuildString, Each, Echo, First, Get, Keep, Last, Let, Nth, StrCollect, Wrap, }; -use nu_engine::{ - run_block, whole_stream_command, Command, CommandArgs, EvaluationContext, WholeStreamCommand, -}; -use nu_stream::{InputStream, OutputStream}; +use nu_engine::{run_block, whole_stream_command, Command, EvaluationContext, WholeStreamCommand}; +use nu_stream::InputStream; -use async_trait::async_trait; use futures::executor::block_on; -use serde::Deserialize; pub fn test_examples(cmd: Command) -> Result<(), ShellError> { let examples = cmd.examples(); @@ -29,8 +29,8 @@ pub fn test_examples(cmd: Command) -> Result<(), ShellError> { let base_context = basic_evaluation_context()?; base_context.add_commands(vec![ - // Mocks - whole_stream_command(MockLs {}), + // Command Doubles + whole_stream_command(DoubleLs {}), // Minimal restricted commands to aid in testing whole_stream_command(Echo {}), whole_stream_command(BuildString {}), @@ -150,9 +150,9 @@ pub fn test_anchors(cmd: Command) -> Result<(), ShellError> { base_context.add_commands(vec![ // Minimal restricted commands to aid in testing - whole_stream_command(MockCommand {}), - whole_stream_command(MockEcho {}), - whole_stream_command(MockLs {}), + whole_stream_command(StubOpen {}), + whole_stream_command(DoubleEcho {}), + whole_stream_command(DoubleLs {}), whole_stream_command(BuildString {}), whole_stream_command(First {}), whole_stream_command(Get {}), @@ -167,7 +167,7 @@ pub fn test_anchors(cmd: Command) -> Result<(), ShellError> { ]); for sample_pipeline in examples { - let pipeline_with_anchor = format!("mock --open --path | {}", sample_pipeline.example); + let pipeline_with_anchor = format!("stub open --path | {}", sample_pipeline.example); let mut ctx = base_context.clone(); @@ -262,232 +262,3 @@ fn values_equal(expected: &Value, actual: &Value) -> bool { fn is_anchor_carried(actual: &Value, anchor: AnchorLocation) -> bool { actual.tag.anchor() == Some(anchor) } - -#[derive(Deserialize)] -struct Arguments { - path: Option, - open: bool, -} - -struct MockCommand; - -#[async_trait] -impl WholeStreamCommand for MockCommand { - fn name(&self) -> &str { - "mock" - } - - fn signature(&self) -> Signature { - Signature::build("mock") - .switch("open", "fake opening sources", Some('o')) - .switch("path", "file open", Some('p')) - } - - fn usage(&self) -> &str { - "Generates tables and metadata that mimics behavior of real commands in controlled ways." - } - - async fn run(&self, args: CommandArgs) -> Result { - let name_tag = args.call_info.name_tag.clone(); - - let ( - Arguments { - path: mocked_path, - open: open_mock, - }, - _input, - ) = args.process().await?; - - let out = UntaggedValue::string("Yehuda Katz in Ecuador"); - - if open_mock { - if let Some(true) = mocked_path { - return Ok(OutputStream::one(Ok(ReturnSuccess::Value(Value { - value: out, - tag: Tag { - anchor: Some(mock_path()), - span: name_tag.span, - }, - })))); - } - } - - Ok(OutputStream::one(Ok(ReturnSuccess::Value( - out.into_value(name_tag), - )))) - } -} - -struct MockEcho; - -#[derive(Deserialize)] -struct MockEchoArgs { - pub rest: Vec, -} - -#[async_trait] -impl WholeStreamCommand for MockEcho { - fn name(&self) -> &str { - "echo" - } - - fn signature(&self) -> Signature { - Signature::build("echo").rest(SyntaxShape::Any, "the values to echo") - } - - fn usage(&self) -> &str { - "Mock echo." - } - - async fn run(&self, args: CommandArgs) -> Result { - let name_tag = args.call_info.name_tag.clone(); - let (MockEchoArgs { rest }, input) = args.process().await?; - - let mut base_value = UntaggedValue::string("Yehuda Katz in Ecuador").into_value(name_tag); - let input: Vec = input.collect().await; - - if let Some(first) = input.get(0) { - base_value = first.clone() - } - - let stream = rest.into_iter().map(move |i| { - let base_value = base_value.clone(); - match i.as_string() { - Ok(s) => OutputStream::one(Ok(ReturnSuccess::Value(Value { - value: UntaggedValue::Primitive(Primitive::String(s)), - tag: base_value.tag, - }))), - _ => match i { - Value { - value: UntaggedValue::Table(table), - .. - } => { - if table.len() == 1 && table[0].is_table() { - let mut values: Vec = - table[0].table_entries().map(Clone::clone).collect(); - - for v in values.iter_mut() { - v.tag = base_value.tag(); - } - - let subtable = - vec![UntaggedValue::Table(values).into_value(base_value.tag())]; - - futures::stream::iter(subtable.into_iter().map(ReturnSuccess::value)) - .to_output_stream() - } else { - futures::stream::iter( - table - .into_iter() - .map(move |mut v| { - v.tag = base_value.tag(); - v - }) - .map(ReturnSuccess::value), - ) - .to_output_stream() - } - } - _ => OutputStream::one(Ok(ReturnSuccess::Value(Value { - value: i.value.clone(), - tag: base_value.tag, - }))), - }, - } - }); - - Ok(futures::stream::iter(stream).flatten().to_output_stream()) - } -} - -struct MockLs; - -#[async_trait] -impl WholeStreamCommand for MockLs { - fn name(&self) -> &str { - "ls" - } - - fn signature(&self) -> Signature { - Signature::build("ls") - } - - fn usage(&self) -> &str { - "Mock ls." - } - - async fn run(&self, args: CommandArgs) -> Result { - let name_tag = args.call_info.name_tag.clone(); - - let mut base_value = - UntaggedValue::string("Andrés N. Robalino in Portland").into_value(name_tag); - let input: Vec = args.input.collect().await; - - if let Some(first) = input.get(0) { - base_value = first.clone() - } - - Ok(futures::stream::iter( - file_listing() - .iter() - .map(|row| Value { - value: row.value.clone(), - tag: base_value.tag.clone(), - }) - .collect::>() - .into_iter() - .map(ReturnSuccess::value), - ) - .to_output_stream()) - } -} - -fn int(s: impl Into) -> Value { - UntaggedValue::int(s).into_untagged_value() -} - -fn string(input: impl Into) -> Value { - UntaggedValue::string(input.into()).into_untagged_value() -} - -fn date(input: impl Into) -> Value { - let key = input.into().tagged_unknown(); - crate::value::Date::naive_from_str(key.borrow_tagged()) - .expect("date from string failed") - .into_untagged_value() -} - -fn file_listing() -> Vec { - vec![ - row! { - "name".to_string() => string("Andrés.txt"), - "type".to_string() => string("File"), - "chickens".to_string() => int(10), - "modified".to_string() => date("2019-07-23") - }, - row! { - "name".to_string() => string("Jonathan"), - "type".to_string() => string("Dir"), - "chickens".to_string() => int(5), - "modified".to_string() => date("2019-07-23") - }, - row! { - "name".to_string() => string("Andrés.txt"), - "type".to_string() => string("File"), - "chickens".to_string() => int(20), - "modified".to_string() => date("2019-09-24") - }, - row! { - "name".to_string() => string("Yehuda"), - "type".to_string() => string("Dir"), - "chickens".to_string() => int(4), - "modified".to_string() => date("2019-09-24") - }, - ] -} - -fn mock_path() -> AnchorLocation { - let path = String::from("path/to/las_best_arepas_in_the_world.txt"); - - AnchorLocation::File(path) -} diff --git a/crates/nu-command/src/examples/double_echo.rs b/crates/nu-command/src/examples/double_echo.rs new file mode 100644 index 0000000000..2efcf0b470 --- /dev/null +++ b/crates/nu-command/src/examples/double_echo.rs @@ -0,0 +1,91 @@ +use nu_errors::ShellError; + +use nu_engine::{CommandArgs, WholeStreamCommand}; +use nu_protocol::{Primitive, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value}; +use nu_stream::{OutputStream, ToOutputStream}; + +use async_trait::async_trait; +use futures::StreamExt; +use serde::Deserialize; + +pub struct Command; + +#[derive(Deserialize)] +struct Arguments { + pub rest: Vec, +} + +#[async_trait] +impl WholeStreamCommand for Command { + fn name(&self) -> &str { + "echo" + } + + fn signature(&self) -> Signature { + Signature::build("echo").rest(SyntaxShape::Any, "the values to echo") + } + + fn usage(&self) -> &str { + "Mock echo." + } + + async fn run(&self, args: CommandArgs) -> Result { + let name_tag = args.call_info.name_tag.clone(); + let (Arguments { rest }, input) = args.process().await?; + + let mut base_value = UntaggedValue::string("Yehuda Katz in Ecuador").into_value(name_tag); + let input: Vec = input.collect().await; + + if let Some(first) = input.get(0) { + base_value = first.clone() + } + + let stream = rest.into_iter().map(move |i| { + let base_value = base_value.clone(); + match i.as_string() { + Ok(s) => OutputStream::one(Ok(ReturnSuccess::Value(Value { + value: UntaggedValue::Primitive(Primitive::String(s)), + tag: base_value.tag, + }))), + _ => match i { + Value { + value: UntaggedValue::Table(table), + .. + } => { + if table.len() == 1 && table[0].is_table() { + let mut values: Vec = + table[0].table_entries().map(Clone::clone).collect(); + + for v in values.iter_mut() { + v.tag = base_value.tag(); + } + + let subtable = + vec![UntaggedValue::Table(values).into_value(base_value.tag())]; + + futures::stream::iter(subtable.into_iter().map(ReturnSuccess::value)) + .to_output_stream() + } else { + futures::stream::iter( + table + .into_iter() + .map(move |mut v| { + v.tag = base_value.tag(); + v + }) + .map(ReturnSuccess::value), + ) + .to_output_stream() + } + } + _ => OutputStream::one(Ok(ReturnSuccess::Value(Value { + value: i.value.clone(), + tag: base_value.tag, + }))), + }, + } + }); + + Ok(futures::stream::iter(stream).flatten().to_output_stream()) + } +} diff --git a/crates/nu-command/src/examples/double_ls.rs b/crates/nu-command/src/examples/double_ls.rs new file mode 100644 index 0000000000..382d493b37 --- /dev/null +++ b/crates/nu-command/src/examples/double_ls.rs @@ -0,0 +1,51 @@ +use crate::examples::sample::ls::file_listing; + +use nu_engine::{CommandArgs, WholeStreamCommand}; +use nu_errors::ShellError; +use nu_protocol::{ReturnSuccess, Signature, UntaggedValue, Value}; +use nu_stream::{OutputStream, ToOutputStream}; + +use async_trait::async_trait; +use futures::StreamExt; + +pub struct Command; + +#[async_trait] +impl WholeStreamCommand for Command { + fn name(&self) -> &str { + "ls" + } + + fn signature(&self) -> Signature { + Signature::build("ls") + } + + fn usage(&self) -> &str { + "Mock ls." + } + + async fn run(&self, args: CommandArgs) -> Result { + let name_tag = args.call_info.name_tag.clone(); + + let mut base_value = + UntaggedValue::string("Andrés N. Robalino in Portland").into_value(name_tag); + let input: Vec = args.input.collect().await; + + if let Some(first) = input.get(0) { + base_value = first.clone() + } + + Ok(futures::stream::iter( + file_listing() + .iter() + .map(|row| Value { + value: row.value.clone(), + tag: base_value.tag.clone(), + }) + .collect::>() + .into_iter() + .map(ReturnSuccess::value), + ) + .to_output_stream()) + } +} diff --git a/crates/nu-command/src/examples/sample.rs b/crates/nu-command/src/examples/sample.rs new file mode 100644 index 0000000000..903a511d5a --- /dev/null +++ b/crates/nu-command/src/examples/sample.rs @@ -0,0 +1,35 @@ +use nu_protocol::{row, Value}; +use nu_test_support::value::{date, int, string}; + +pub mod ls { + use super::*; + + pub fn file_listing() -> Vec { + vec![ + row! { + "name".to_string() => string("Andrés.txt"), + "type".to_string() => string("File"), + "chickens".to_string() => int(10), + "modified".to_string() => date("2019-07-23") + }, + row! { + "name".to_string() => string("Jonathan"), + "type".to_string() => string("Dir"), + "chickens".to_string() => int(5), + "modified".to_string() => date("2019-07-23") + }, + row! { + "name".to_string() => string("Andrés.txt"), + "type".to_string() => string("File"), + "chickens".to_string() => int(20), + "modified".to_string() => date("2019-09-24") + }, + row! { + "name".to_string() => string("Yehuda"), + "type".to_string() => string("Dir"), + "chickens".to_string() => int(4), + "modified".to_string() => date("2019-09-24") + }, + ] + } +} diff --git a/crates/nu-command/src/examples/stub_generate.rs b/crates/nu-command/src/examples/stub_generate.rs new file mode 100644 index 0000000000..b55862b54e --- /dev/null +++ b/crates/nu-command/src/examples/stub_generate.rs @@ -0,0 +1,58 @@ +use nu_engine::{CommandArgs, WholeStreamCommand}; +use nu_errors::ShellError; +use nu_protocol::{ReturnSuccess, Signature, UntaggedValue, Value}; +use nu_source::{AnchorLocation, Tag}; +use nu_stream::OutputStream; + +use async_trait::async_trait; +use serde::Deserialize; + +pub struct Command; + +#[derive(Deserialize)] +struct Arguments { + path: Option, +} + +#[async_trait] +impl WholeStreamCommand for Command { + fn name(&self) -> &str { + "stub open" + } + + fn signature(&self) -> Signature { + Signature::build("stub open").switch("path", "Add a mocked path", Some('p')) + } + + fn usage(&self) -> &str { + "Generates tables and metadata that mimics behavior of real commands in controlled ways." + } + + async fn run(&self, args: CommandArgs) -> Result { + let name_tag = args.call_info.name_tag.clone(); + + let (Arguments { path: mocked_path }, _input) = args.process().await?; + + let out = UntaggedValue::string("Yehuda Katz in Ecuador"); + + if let Some(true) = mocked_path { + Ok(OutputStream::one(Ok(ReturnSuccess::Value(Value { + value: out, + tag: Tag { + anchor: Some(mock_path()), + span: name_tag.span, + }, + })))) + } else { + Ok(OutputStream::one(Ok(ReturnSuccess::Value( + out.into_value(name_tag), + )))) + } + } +} + +pub fn mock_path() -> AnchorLocation { + let path = String::from("path/to/las_best_arepas_in_the_world.txt"); + + AnchorLocation::File(path) +}