Merge pull request #833 from andrasio/recon

Recon
This commit is contained in:
Jonathan Turner 2019-10-16 05:00:57 +13:00 committed by GitHub
commit f2d54f201d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 183 additions and 72 deletions

View File

@ -258,7 +258,6 @@ pub async fn cli() -> Result<(), Box<dyn Error>> {
whole_stream_command(Next), whole_stream_command(Next),
whole_stream_command(Previous), whole_stream_command(Previous),
whole_stream_command(Debug), whole_stream_command(Debug),
whole_stream_command(Lines),
whole_stream_command(Shells), whole_stream_command(Shells),
whole_stream_command(SplitColumn), whole_stream_command(SplitColumn),
whole_stream_command(SplitRow), whole_stream_command(SplitRow),
@ -277,6 +276,7 @@ pub async fn cli() -> Result<(), Box<dyn Error>> {
whole_stream_command(ToYAML), whole_stream_command(ToYAML),
whole_stream_command(SortBy), whole_stream_command(SortBy),
whole_stream_command(Tags), whole_stream_command(Tags),
whole_stream_command(Count),
whole_stream_command(First), whole_stream_command(First),
whole_stream_command(Last), whole_stream_command(Last),
whole_stream_command(Env), whole_stream_command(Env),

View File

@ -8,6 +8,7 @@ pub(crate) mod classified;
pub(crate) mod clip; pub(crate) mod clip;
pub(crate) mod command; pub(crate) mod command;
pub(crate) mod config; pub(crate) mod config;
pub(crate) mod count;
pub(crate) mod cp; pub(crate) mod cp;
pub(crate) mod date; pub(crate) mod date;
pub(crate) mod debug; pub(crate) mod debug;
@ -78,6 +79,7 @@ pub(crate) use command::{
pub(crate) use classified::ClassifiedCommand; pub(crate) use classified::ClassifiedCommand;
pub(crate) use config::Config; pub(crate) use config::Config;
pub(crate) use count::Count;
pub(crate) use cp::Cpy; pub(crate) use cp::Cpy;
pub(crate) use date::Date; pub(crate) use date::Date;
pub(crate) use debug::Debug; pub(crate) use debug::Debug;

View File

@ -251,7 +251,7 @@ impl ExternalCommand {
) )
} else { } else {
ShellError::labeled_error( ShellError::labeled_error(
"Error: $it needs string data", "$it needs string data",
"given something else", "given something else",
self.name_tag.clone(), self.name_tag.clone(),
) )

View File

@ -70,9 +70,9 @@ pub fn config(
if let Some(v) = get { if let Some(v) = get {
let key = v.to_string(); let key = v.to_string();
let value = result.get(&key).ok_or_else(|| { let value = result
ShellError::labeled_error(&format!("Missing key in config"), "key", v.tag()) .get(&key)
})?; .ok_or_else(|| ShellError::labeled_error("Missing key in config", "key", v.tag()))?;
let mut results = VecDeque::new(); let mut results = VecDeque::new();
@ -121,7 +121,7 @@ pub fn config(
config::write(&result, &configuration)?; config::write(&result, &configuration)?;
} else { } else {
return Err(ShellError::labeled_error( return Err(ShellError::labeled_error(
"{} does not exist in config", "Key does not exist in config",
"key", "key",
v.tag(), v.tag(),
)); ));

46
src/commands/count.rs Normal file
View File

@ -0,0 +1,46 @@
use crate::commands::WholeStreamCommand;
use crate::data::Value;
use crate::errors::ShellError;
use crate::parser::CommandRegistry;
use crate::prelude::*;
use futures::stream::StreamExt;
pub struct Count;
#[derive(Deserialize)]
pub struct CountArgs {}
impl WholeStreamCommand for Count {
fn name(&self) -> &str {
"count"
}
fn signature(&self) -> Signature {
Signature::build("count")
}
fn usage(&self) -> &str {
"Show the total number of rows."
}
fn run(
&self,
args: CommandArgs,
registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> {
args.process(registry, count)?.run()
}
}
pub fn count(
CountArgs {}: CountArgs,
RunnableContext { input, name, .. }: RunnableContext,
) -> Result<OutputStream, ShellError> {
let stream = async_stream! {
let rows: Vec<Tagged<Value>> = input.values.collect().await;
yield ReturnSuccess::value(Value::int(rows.len()).tagged(name))
};
Ok(stream.to_output_stream())
}

View File

@ -7,7 +7,7 @@ pub struct First;
#[derive(Deserialize)] #[derive(Deserialize)]
pub struct FirstArgs { pub struct FirstArgs {
amount: Tagged<u64>, rows: Option<Tagged<u64>>,
} }
impl WholeStreamCommand for First { impl WholeStreamCommand for First {
@ -16,7 +16,7 @@ impl WholeStreamCommand for First {
} }
fn signature(&self) -> Signature { fn signature(&self) -> Signature {
Signature::build("first").required("amount", SyntaxShape::Int) Signature::build("first").optional("rows", SyntaxShape::Int)
} }
fn usage(&self) -> &str { fn usage(&self) -> &str {
@ -33,8 +33,16 @@ impl WholeStreamCommand for First {
} }
fn first( fn first(
FirstArgs { amount }: FirstArgs, FirstArgs { rows }: FirstArgs,
context: RunnableContext, context: RunnableContext,
) -> Result<OutputStream, ShellError> { ) -> Result<OutputStream, ShellError> {
Ok(OutputStream::from_input(context.input.values.take(*amount))) let rows_desired = if let Some(quantity) = rows {
*quantity
} else {
1
};
Ok(OutputStream::from_input(
context.input.values.take(rows_desired),
))
} }

View File

@ -7,7 +7,7 @@ pub struct Last;
#[derive(Deserialize)] #[derive(Deserialize)]
pub struct LastArgs { pub struct LastArgs {
amount: Tagged<u64>, rows: Option<Tagged<u64>>,
} }
impl WholeStreamCommand for Last { impl WholeStreamCommand for Last {
@ -16,7 +16,7 @@ impl WholeStreamCommand for Last {
} }
fn signature(&self) -> Signature { fn signature(&self) -> Signature {
Signature::build("last").required("amount", SyntaxShape::Number) Signature::build("last").optional("rows", SyntaxShape::Number)
} }
fn usage(&self) -> &str { fn usage(&self) -> &str {
@ -32,13 +32,17 @@ impl WholeStreamCommand for Last {
} }
} }
fn last( fn last(LastArgs { rows }: LastArgs, context: RunnableContext) -> Result<OutputStream, ShellError> {
LastArgs { amount }: LastArgs,
context: RunnableContext,
) -> Result<OutputStream, ShellError> {
let stream = async_stream! { let stream = async_stream! {
let v: Vec<_> = context.input.into_vec().await; let v: Vec<_> = context.input.into_vec().await;
let count = (*amount as usize);
let rows_desired = if let Some(quantity) = rows {
*quantity
} else {
1
};
let count = (rows_desired as usize);
if count < v.len() { if count < v.len() {
let k = v.len() - count; let k = v.len() - count;
for x in v[k..].iter() { for x in v[k..].iter() {

View File

@ -21,8 +21,8 @@ async fn docker(sub_command: &String, name: Tag) -> Result<Vec<Tagged<Value>>, S
"images" => docker_images(name), "images" => docker_images(name),
_ => Err(ShellError::labeled_error( _ => Err(ShellError::labeled_error(
"Unsupported Docker command", "Unsupported Docker command",
format!("'{}'?", sub_command), "unknown docker command",
name.span, name,
)), )),
} }
} }
@ -46,7 +46,7 @@ fn process_docker_output(cmd_output: &str, tag: Tag) -> Result<Vec<Tagged<Value>
.filter(|s| s.trim() != "") .filter(|s| s.trim() != "")
.collect(); .collect();
let mut dict = TaggedDictBuilder::new(tag); let mut dict = TaggedDictBuilder::new(&tag);
for (i, v) in values.iter().enumerate() { for (i, v) in values.iter().enumerate() {
dict.insert(header[i].to_string(), Value::string(v.trim().to_string())); dict.insert(header[i].to_string(), Value::string(v.trim().to_string()));
} }
@ -92,18 +92,13 @@ impl Plugin for Docker {
if let Some(args) = callinfo.args.positional { if let Some(args) = callinfo.args.positional {
match &args[0] { match &args[0] {
Tagged { Tagged {
item: Value::Primitive(Primitive::String(s)), item: Value::Primitive(Primitive::String(command)),
.. ..
} => match block_on(docker(&s, callinfo.name_tag)) { } => match block_on(docker(&command, args[0].tag())) {
Ok(v) => return Ok(v.into_iter().map(ReturnSuccess::value).collect()), Ok(v) => return Ok(v.into_iter().map(ReturnSuccess::value).collect()),
Err(e) => return Err(e), Err(e) => return Err(e),
}, },
_ => { _ => return Err(ShellError::type_error("string", args[0].tagged_type_name())),
return Err(ShellError::string(format!(
"Unrecognized type in params: {:?}",
args[0]
)))
}
} }
} }

View File

@ -7,26 +7,21 @@ use helpers::{Playground, Stub::*};
fn ls_lists_regular_files() { fn ls_lists_regular_files() {
Playground::setup("ls_test_1", |dirs, sandbox| { Playground::setup("ls_test_1", |dirs, sandbox| {
sandbox.with_files(vec![ sandbox.with_files(vec![
EmptyFile("yehuda.10.txt"), EmptyFile("yehuda.txt"),
EmptyFile("jonathan.10.txt"), EmptyFile("jonathan.txt"),
EmptyFile("andres.10.txt"), EmptyFile("andres.txt"),
]); ]);
let actual = nu!( let actual = nu!(
cwd: dirs.test(), h::pipeline( cwd: dirs.test(), h::pipeline(
r#" r#"
ls ls
| get name | count
| lines
| split-column "."
| get Column2
| str --to-int
| sum
| echo $it | echo $it
"# "#
)); ));
assert_eq!(actual, "30"); assert_eq!(actual, "3");
}) })
} }
@ -34,22 +29,17 @@ fn ls_lists_regular_files() {
fn ls_lists_regular_files_using_asterisk_wildcard() { fn ls_lists_regular_files_using_asterisk_wildcard() {
Playground::setup("ls_test_2", |dirs, sandbox| { Playground::setup("ls_test_2", |dirs, sandbox| {
sandbox.with_files(vec![ sandbox.with_files(vec![
EmptyFile("los.1.txt"), EmptyFile("los.txt"),
EmptyFile("tres.1.txt"), EmptyFile("tres.txt"),
EmptyFile("amigos.1.txt"), EmptyFile("amigos.txt"),
EmptyFile("arepas.1.clu"), EmptyFile("arepas.clu"),
]); ]);
let actual = nu!( let actual = nu!(
cwd: dirs.test(), h::pipeline( cwd: dirs.test(), h::pipeline(
r#" r#"
ls *.txt ls *.txt
| get name | count
| lines
| split-column "."
| get Column2
| str --to-int
| sum
| echo $it | echo $it
"# "#
)); ));
@ -72,16 +62,11 @@ fn ls_lists_regular_files_using_question_mark_wildcard() {
cwd: dirs.test(), h::pipeline( cwd: dirs.test(), h::pipeline(
r#" r#"
ls *.??.txt ls *.??.txt
| get name | count
| lines
| split-column "."
| get Column2
| str --to-int
| sum
| echo $it | echo $it
"# "#
)); ));
assert_eq!(actual, "30"); assert_eq!(actual, "3");
}) })
} }

View File

@ -7,38 +7,109 @@ use helpers::{Playground, Stub::*};
fn first_gets_first_rows_by_amount() { fn first_gets_first_rows_by_amount() {
Playground::setup("first_test_1", |dirs, sandbox| { Playground::setup("first_test_1", |dirs, sandbox| {
sandbox.with_files(vec![ sandbox.with_files(vec![
EmptyFile("los.1.txt"), EmptyFile("los.txt"),
EmptyFile("tres.1.txt"), EmptyFile("tres.txt"),
EmptyFile("amigos.1.txt"), EmptyFile("amigos.txt"),
EmptyFile("arepas.1.clu"), EmptyFile("arepas.clu"),
]); ]);
let actual = nu!( let actual = nu!(
cwd: dirs.test(), h::pipeline( cwd: dirs.test(), h::pipeline(
r#" r#"
ls ls
| get name | first 3
| first 2 | count
| split-column "."
| get Column2
| str --to-int
| sum
| echo $it | echo $it
"# "#
)); ));
assert_eq!(actual, "2"); assert_eq!(actual, "3");
}) })
} }
#[test] #[test]
fn first_requires_an_amount() { fn first_gets_all_rows_if_amount_higher_than_all_rows() {
Playground::setup("first_test_2", |dirs, _| { Playground::setup("first_test_2", |dirs, sandbox| {
let actual = nu_error!( sandbox.with_files(vec![
cwd: dirs.test(), "ls | first" EmptyFile("los.txt"),
); EmptyFile("tres.txt"),
EmptyFile("amigos.txt"),
EmptyFile("arepas.clu"),
]);
assert!(actual.contains("requires amount parameter")); let actual = nu!(
cwd: dirs.test(), h::pipeline(
r#"
ls
| first 99
| count
| echo $it
"#
));
assert_eq!(actual, "4");
})
}
#[test]
fn first_gets_first_row_when_no_amount_given() {
Playground::setup("first_test_3", |dirs, sandbox| {
sandbox.with_files(vec![EmptyFile("caballeros.txt"), EmptyFile("arepas.clu")]);
let actual = nu!(
cwd: dirs.test(), h::pipeline(
r#"
ls
| first
| count
| echo $it
"#
));
assert_eq!(actual, "1");
})
}
#[test]
fn last_gets_last_rows_by_amount() {
Playground::setup("last_test_1", |dirs, sandbox| {
sandbox.with_files(vec![
EmptyFile("los.txt"),
EmptyFile("tres.txt"),
EmptyFile("amigos.txt"),
EmptyFile("arepas.clu"),
]);
let actual = nu!(
cwd: dirs.test(), h::pipeline(
r#"
ls
| last 3
| count
| echo $it
"#
));
assert_eq!(actual, "3");
})
}
#[test]
fn last_gets_last_row_when_no_amount_given() {
Playground::setup("last_test_2", |dirs, sandbox| {
sandbox.with_files(vec![EmptyFile("caballeros.txt"), EmptyFile("arepas.clu")]);
let actual = nu!(
cwd: dirs.test(), h::pipeline(
r#"
ls
| last
| count
| echo $it
"#
));
assert_eq!(actual, "1");
}) })
} }