diff --git a/crates/nu-cli/src/commands/histogram.rs b/crates/nu-cli/src/commands/histogram.rs index a263e8a7a..231529d58 100644 --- a/crates/nu-cli/src/commands/histogram.rs +++ b/crates/nu-cli/src/commands/histogram.rs @@ -1,16 +1,13 @@ use crate::commands::WholeStreamCommand; use crate::prelude::*; use nu_errors::ShellError; -use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, TaggedDictBuilder, UntaggedValue, Value}; +use nu_protocol::{ + ColumnPath, ReturnSuccess, Signature, SyntaxShape, TaggedDictBuilder, UntaggedValue, Value, +}; use nu_source::Tagged; pub struct Histogram; -#[derive(Deserialize)] -pub struct HistogramArgs { - rest: Vec>, -} - #[async_trait] impl WholeStreamCommand for Histogram { fn name(&self) -> &str { @@ -18,10 +15,17 @@ impl WholeStreamCommand for Histogram { } fn signature(&self) -> Signature { - Signature::build("histogram").rest( - SyntaxShape::String, - "column name to give the histogram's frequency column", - ) + Signature::build("histogram") + .named( + "use", + SyntaxShape::ColumnPath, + "Use data at the column path given as valuator", + None, + ) + .rest( + SyntaxShape::ColumnPath, + "column name to give the histogram's frequency column", + ) } fn usage(&self) -> &str { @@ -64,22 +68,37 @@ pub async fn histogram( ) -> Result { let registry = registry.clone(); let name = args.call_info.name_tag.clone(); + let (input, args) = args.evaluate_once(®istry).await?.parts(); - let (HistogramArgs { rest: mut columns }, input) = args.process(®istry).await?; let values: Vec = input.collect().await; - let column_grouper = if !columns.is_empty() { - Some(columns.remove(0)) + let mut columns = args + .positional_iter() + .map(|c| c.as_column_path()) + .filter_map(Result::ok) + .collect::>(); + + let evaluate_with = if let Some(path) = args.get("use") { + Some(evaluator(path.as_column_path()?.item)) } else { None }; - let column_names_supplied: Vec<_> = columns.iter().map(|f| f.item.clone()).collect(); - - let frequency_column_name = if column_names_supplied.is_empty() { - "frequency".to_string() + let column_grouper = if !columns.is_empty() { + match columns.remove(0).split_last() { + Some((key, _)) => Some(key.as_string().tagged(&name)), + None => None, + } } else { - column_names_supplied[0].clone() + None + }; + + let frequency_column_name = if columns.is_empty() { + "frequency".to_string() + } else if let Some((key, _)) = columns[0].split_last() { + key.as_string() + } else { + "frecuency".to_string() }; let column = if let Some(ref column) = column_grouper { @@ -94,7 +113,7 @@ pub async fn histogram( grouper: Some(Box::new(move |_, _| Ok(String::from("frequencies")))), splitter: Some(splitter(column_grouper)), format: None, - eval: &None, + eval: &evaluate_with, }, &name, )?; @@ -155,6 +174,23 @@ pub async fn histogram( .to_output_stream()) } +fn evaluator(by: ColumnPath) -> Box Result + Send> { + Box::new(move |_: usize, value: &Value| { + let path = by.clone(); + + let eval = nu_value_ext::get_data_by_column_path( + value, + &path, + Box::new(move |(_, _, error)| error), + ); + + match eval { + Ok(with_value) => Ok(with_value), + Err(reason) => Err(reason), + } + }) +} + fn splitter( by: Option>, ) -> Box Result + Send> { diff --git a/crates/nu-cli/src/data/value.rs b/crates/nu-cli/src/data/value.rs index c4d22eb18..42e9e8214 100644 --- a/crates/nu-cli/src/data/value.rs +++ b/crates/nu-cli/src/data/value.rs @@ -62,6 +62,43 @@ fn zero_division_error() -> UntaggedValue { UntaggedValue::Error(ShellError::untagged_runtime_error("division by zero")) } +pub fn unsafe_compute_values( + operator: Operator, + left: &UntaggedValue, + right: &UntaggedValue, +) -> Result { + let computed = compute_values(operator, left, right); + + if computed.is_ok() { + return computed; + } + + match (left, right) { + (UntaggedValue::Primitive(lhs), UntaggedValue::Primitive(rhs)) => match (lhs, rhs) { + (Primitive::Filesize(x), Primitive::Int(y)) => match operator { + Operator::Plus => Ok(UntaggedValue::Primitive(Primitive::Int(x + y))), + Operator::Minus => Ok(UntaggedValue::Primitive(Primitive::Int(x - y))), + Operator::Multiply => Ok(UntaggedValue::Primitive(Primitive::Int(x * y))), + Operator::Divide => Ok(UntaggedValue::Primitive(Primitive::Decimal( + bigdecimal::BigDecimal::from(*x) / bigdecimal::BigDecimal::from(y.clone()), + ))), + _ => Err((left.type_name(), right.type_name())), + }, + (Primitive::Int(x), Primitive::Filesize(y)) => match operator { + Operator::Plus => Ok(UntaggedValue::Primitive(Primitive::Int(x + y))), + Operator::Minus => Ok(UntaggedValue::Primitive(Primitive::Int(x - y))), + Operator::Multiply => Ok(UntaggedValue::Primitive(Primitive::Int(x * y))), + Operator::Divide => Ok(UntaggedValue::Primitive(Primitive::Decimal( + bigdecimal::BigDecimal::from(x.clone()) / bigdecimal::BigDecimal::from(*y), + ))), + _ => Err((left.type_name(), right.type_name())), + }, + _ => Err((left.type_name(), right.type_name())), + }, + _ => Err((left.type_name(), right.type_name())), + } +} + pub fn compute_values( operator: Operator, left: &UntaggedValue,