Make sum plugin as internal command. (#1501)

This commit is contained in:
Andrés N. Robalino
2020-03-18 18:46:00 -05:00
committed by GitHub
parent 390deb4ff7
commit 21a543a901
18 changed files with 278 additions and 204 deletions

View File

@ -313,6 +313,7 @@ pub fn create_default_context(
whole_stream_command(Pivot),
// Data processing
whole_stream_command(Histogram),
whole_stream_command(Sum),
// File format output
whole_stream_command(ToBSON),
whole_stream_command(ToCSV),

View File

@ -81,6 +81,7 @@ pub(crate) mod sort_by;
pub(crate) mod split_by;
pub(crate) mod split_column;
pub(crate) mod split_row;
pub(crate) mod sum;
#[allow(unused)]
pub(crate) mod t_sort_by;
pub(crate) mod table;
@ -186,6 +187,7 @@ pub(crate) use sort_by::SortBy;
pub(crate) use split_by::SplitBy;
pub(crate) use split_column::SplitColumn;
pub(crate) use split_row::SplitRow;
pub(crate) use sum::Sum;
#[allow(unused_imports)]
pub(crate) use t_sort_by::TSortBy;
pub(crate) use table::Table;

View File

@ -0,0 +1,55 @@
use crate::commands::WholeStreamCommand;
use crate::prelude::*;
use crate::utils::data_processing::{reducer_for, Reduce};
use nu_errors::ShellError;
use nu_protocol::{ReturnSuccess, ReturnValue, Signature, Value};
use num_traits::identities::Zero;
pub struct Sum;
impl WholeStreamCommand for Sum {
fn name(&self) -> &str {
"sum"
}
fn signature(&self) -> Signature {
Signature::build("sum")
}
fn usage(&self) -> &str {
"Sums the values."
}
fn run(
&self,
args: CommandArgs,
registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> {
sum(RunnableContext {
input: args.input,
commands: registry.clone(),
shell_manager: args.shell_manager,
host: args.host,
source: args.call_info.source,
ctrl_c: args.ctrl_c,
name: args.call_info.name_tag,
})
}
}
fn sum(RunnableContext { mut input, .. }: RunnableContext) -> Result<OutputStream, ShellError> {
let stream = async_stream! {
let mut values = input.drain_vec().await;
let action = reducer_for(Reduce::Sum);
match action(Value::zero(), values) {
Ok(total) => yield ReturnSuccess::value(total),
Err(err) => yield Err(err),
}
};
let stream: BoxStream<'static, ReturnValue> = stream.boxed();
Ok(stream.to_output_stream())
}

View File

@ -1,10 +1,11 @@
use crate::data::value::compare_values;
use crate::data::TaggedListBuilder;
use chrono::{DateTime, NaiveDate, Utc};
use nu_errors::ShellError;
use nu_parser::CompareOperator;
use nu_protocol::{Primitive, TaggedDictBuilder, UntaggedValue, Value};
use nu_source::{SpannedItem, Tag, Tagged, TaggedItem};
use nu_value_ext::{get_data_by_key, ValueExt};
use num_bigint::BigInt;
use num_traits::Zero;
pub fn columns_sorted(
@ -196,44 +197,31 @@ pub fn evaluate(
Ok(results)
}
fn sum(data: Vec<Value>) -> Result<Value, ShellError> {
let total = data
pub fn sum(data: Vec<Value>) -> Result<Value, ShellError> {
Ok(data
.into_iter()
.fold(Zero::zero(), |acc: BigInt, value| match value {
Value {
value: UntaggedValue::Primitive(Primitive::Int(n)),
..
} => acc + n,
_ => acc,
});
Ok(UntaggedValue::int(total).into_untagged_value())
.fold(Value::zero(), |acc: Value, value| acc + value))
}
fn formula(
acc_begin: BigInt,
calculator: Box<dyn Fn(Vec<Value>) -> Result<Value, ShellError> + 'static>,
) -> Box<dyn Fn(BigInt, Vec<Value>) -> Result<Value, ShellError> + 'static> {
acc_begin: Value,
calculator: Box<dyn Fn(Vec<Value>) -> Result<Value, ShellError> + Send + Sync + 'static>,
) -> Box<dyn Fn(Value, Vec<Value>) -> Result<Value, ShellError> + Send + Sync + 'static> {
Box::new(move |acc, datax| -> Result<Value, ShellError> {
let result = acc * acc_begin.clone();
if let Ok(Value {
value: UntaggedValue::Primitive(Primitive::Int(computed)),
..
}) = calculator(datax)
{
return Ok(UntaggedValue::int(result + computed).into_untagged_value());
match calculator(datax) {
Ok(total) => Ok(result + total),
Err(reason) => Err(reason),
}
Ok(UntaggedValue::int(0).into_untagged_value())
})
}
pub fn reducer_for(
command: Reduce,
) -> Box<dyn Fn(BigInt, Vec<Value>) -> Result<Value, ShellError> + 'static> {
) -> Box<dyn Fn(Value, Vec<Value>) -> Result<Value, ShellError> + Send + Sync + 'static> {
match command {
Reduce::Sum | Reduce::Default => Box::new(formula(Zero::zero(), Box::new(sum))),
Reduce::Sum | Reduce::Default => Box::new(formula(Value::zero(), Box::new(sum))),
}
}
@ -262,7 +250,7 @@ pub fn reduce(
let datasets: Vec<_> = datasets
.iter()
.map(|subsets| {
let acc: BigInt = Zero::zero();
let acc = Value::zero();
match subsets {
Value {
value: UntaggedValue::Table(data),
@ -318,37 +306,48 @@ pub fn map_max(
value: UntaggedValue::Table(datasets),
..
} => {
let datasets: Vec<_> = datasets
let datasets: Vec<Value> = datasets
.iter()
.map(|subsets| match subsets {
Value {
value: UntaggedValue::Table(data),
..
} => {
let data: BigInt =
data.iter().fold(Zero::zero(), |acc, value| match value {
Value {
value: UntaggedValue::Primitive(Primitive::Int(n)),
..
} if *n > acc => n.clone(),
_ => acc,
});
UntaggedValue::int(data).into_value(&tag)
}
} => data.iter().fold(Value::zero(), |acc, value| {
let left = &value.value;
let right = &acc.value;
if let Ok(is_greater_than) =
compare_values(CompareOperator::GreaterThan, left, right)
{
if is_greater_than {
value.clone()
} else {
acc
}
} else {
acc
}
}),
_ => UntaggedValue::int(0).into_value(&tag),
})
.collect();
let datasets: BigInt = datasets
.iter()
.fold(Zero::zero(), |max, value| match value {
Value {
value: UntaggedValue::Primitive(Primitive::Int(n)),
..
} if *n > max => n.clone(),
_ => max,
});
UntaggedValue::int(datasets).into_value(&tag)
datasets.into_iter().fold(Value::zero(), |max, value| {
let left = &value.value;
let right = &max.value;
if let Ok(is_greater_than) =
compare_values(CompareOperator::GreaterThan, left, right)
{
if is_greater_than {
value
} else {
max
}
} else {
max
}
})
}
_ => UntaggedValue::int(-1).into_value(&tag),
};
@ -573,7 +572,7 @@ mod tests {
let action = reducer_for(Reduce::Sum);
assert_eq!(action(Zero::zero(), subject)?, int(3));
assert_eq!(action(Value::zero(), subject)?, int(3));
Ok(())
}

View File

@ -29,6 +29,7 @@ mod save;
mod sort_by;
mod split_by;
mod split_column;
mod sum;
mod touch;
mod uniq;
mod where_;

View File

@ -0,0 +1,30 @@
use nu_test_support::fs::Stub::FileWithContentToBeTrimmed;
use nu_test_support::playground::Playground;
use nu_test_support::{nu, pipeline};
#[test]
fn all() {
Playground::setup("sum_test_1", |dirs, sandbox| {
sandbox.with_files(vec![FileWithContentToBeTrimmed(
"meals.csv",
r#"
description,calories
"1 large egg",90
"1 cup white rice",250
"1 tablespoon fish oil",108
"#,
)]);
let actual = nu!(
cwd: dirs.test(), pipeline(
r#"
open meals.csv
| get calories
| sum
| echo $it
"#
));
assert_eq!(actual, "448");
})
}

View File

@ -4,7 +4,7 @@ use nu_test_support::{nu, pipeline};
#[test]
fn wrap_rows_into_a_row() {
Playground::setup("embed_test_1", |dirs, sandbox| {
Playground::setup("wrap_test_1", |dirs, sandbox| {
sandbox.with_files(vec![FileWithContentToBeTrimmed(
"los_tres_caballeros.txt",
r#"
@ -34,7 +34,7 @@ fn wrap_rows_into_a_row() {
#[test]
fn wrap_rows_into_a_table() {
Playground::setup("embed_test_2", |dirs, sandbox| {
Playground::setup("wrap_test_2", |dirs, sandbox| {
sandbox.with_files(vec![FileWithContentToBeTrimmed(
"los_tres_caballeros.txt",
r#"