mirror of
https://github.com/nushell/nushell.git
synced 2024-11-26 10:23:52 +01:00
histogram: optionally use a valuator for histogram value.
This commit is contained in:
parent
8ff15c46c1
commit
0db4180cea
@ -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<Tagged<String>>,
|
||||
}
|
||||
|
||||
#[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<OutputStream, ShellError> {
|
||||
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<Value> = 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::<Vec<_>>();
|
||||
|
||||
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<dyn Fn(usize, &Value) -> Result<Value, ShellError> + 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<Tagged<String>>,
|
||||
) -> Box<dyn Fn(usize, &Value) -> Result<String, ShellError> + Send> {
|
||||
|
@ -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<UntaggedValue, (&'static str, &'static str)> {
|
||||
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,
|
||||
|
Loading…
Reference in New Issue
Block a user