mirror of
https://github.com/nushell/nushell.git
synced 2025-04-24 21:28:20 +02:00
* move commands, futures.rs, script.rs, utils * move over maybe_print_errors * add nu_command crate references to nu_cli * in commands.rs open up to pub mod from pub(crate) * nu-cli, nu-command, and nu tests are now passing * cargo fmt * clean up nu-cli/src/prelude.rs * code cleanup * for some reason lex.rs was not formatted, may be causing my error * remove mod completion from lib.rs which was not being used along with quickcheck macros * add in allow unused imports * comment out one failing external test; comment out one failing internal test * revert commenting out failing tests; something else might be going on; someone with a windows machine should check and see what is going on with these failing windows tests * Update Cargo.toml Extend the optional features to nu-command Co-authored-by: Jonathan Turner <jonathandturner@users.noreply.github.com>
184 lines
5.9 KiB
Rust
184 lines
5.9 KiB
Rust
use nu_data::value::{compare_values, compute_values};
|
|
use nu_errors::ShellError;
|
|
use nu_protocol::hir::Operator;
|
|
use nu_protocol::{UntaggedValue, Value};
|
|
use nu_source::{SpannedItem, Tag};
|
|
|
|
// Re-usable error messages
|
|
const ERR_EMPTY_DATA: &str = "Cannot perform aggregate math operation on empty data";
|
|
|
|
fn formula(
|
|
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 = match compute_values(Operator::Multiply, &acc, &acc_begin) {
|
|
Ok(v) => v.into_untagged_value(),
|
|
Err((left_type, right_type)) => {
|
|
return Err(ShellError::coerce_error(
|
|
left_type.spanned_unknown(),
|
|
right_type.spanned_unknown(),
|
|
))
|
|
}
|
|
};
|
|
|
|
match calculator(datax) {
|
|
Ok(total) => Ok(match compute_values(Operator::Plus, &result, &total) {
|
|
Ok(v) => v.into_untagged_value(),
|
|
Err((left_type, right_type)) => {
|
|
return Err(ShellError::coerce_error(
|
|
left_type.spanned_unknown(),
|
|
right_type.spanned_unknown(),
|
|
))
|
|
}
|
|
}),
|
|
Err(reason) => Err(reason),
|
|
}
|
|
})
|
|
}
|
|
|
|
pub fn reducer_for(
|
|
command: Reduce,
|
|
) -> Box<dyn Fn(Value, Vec<Value>) -> Result<Value, ShellError> + Send + Sync + 'static> {
|
|
match command {
|
|
Reduce::Default => Box::new(formula(
|
|
UntaggedValue::int(0).into_untagged_value(),
|
|
Box::new(sum),
|
|
)),
|
|
Reduce::Summation => Box::new(|_, values| sum(values)),
|
|
Reduce::Minimum => Box::new(|_, values| min(values)),
|
|
Reduce::Maximum => Box::new(|_, values| max(values)),
|
|
Reduce::Product => Box::new(|_, values| product(values)),
|
|
}
|
|
}
|
|
|
|
#[allow(dead_code)]
|
|
pub enum Reduce {
|
|
Summation,
|
|
Minimum,
|
|
Maximum,
|
|
Product,
|
|
Default,
|
|
}
|
|
|
|
pub fn sum(data: Vec<Value>) -> Result<Value, ShellError> {
|
|
let first_value = data
|
|
.get(0)
|
|
.ok_or_else(|| ShellError::unexpected(ERR_EMPTY_DATA))?;
|
|
|
|
// Generate the initial accumulator value, of the correct type for
|
|
// the incoming data, this will be used in conjunction with the
|
|
// sum aggregator. Currently this is only handling, filesize,
|
|
// and other types are defaulting to an integer.
|
|
let mut acc = if first_value.is_filesize() {
|
|
UntaggedValue::filesize(0u64).into_untagged_value()
|
|
} else {
|
|
UntaggedValue::int(0).into_untagged_value()
|
|
};
|
|
|
|
for value in data {
|
|
match value.value {
|
|
UntaggedValue::Primitive(_) => {
|
|
acc = match compute_values(Operator::Plus, &acc, &value) {
|
|
Ok(v) => v.into_untagged_value(),
|
|
Err((left_type, right_type)) => {
|
|
return Err(ShellError::coerce_error(
|
|
left_type.spanned_unknown(),
|
|
right_type.spanned_unknown(),
|
|
))
|
|
}
|
|
};
|
|
}
|
|
_ => {
|
|
return Err(ShellError::labeled_error(
|
|
"Attempted to compute the sum of a value that cannot be summed.",
|
|
"value appears here",
|
|
value.tag.span,
|
|
))
|
|
}
|
|
}
|
|
}
|
|
Ok(acc)
|
|
}
|
|
|
|
pub fn max(data: Vec<Value>) -> Result<Value, ShellError> {
|
|
let mut biggest = data
|
|
.first()
|
|
.ok_or_else(|| ShellError::unexpected(ERR_EMPTY_DATA))?
|
|
.value
|
|
.clone();
|
|
|
|
for value in data.iter() {
|
|
if let Ok(greater_than) = compare_values(Operator::GreaterThan, &value.value, &biggest) {
|
|
if greater_than {
|
|
biggest = value.value.clone();
|
|
}
|
|
} else {
|
|
return Err(ShellError::unexpected(format!(
|
|
"Could not compare\nleft: {:?}\nright: {:?}",
|
|
biggest, value.value
|
|
)));
|
|
}
|
|
}
|
|
Ok(Value {
|
|
value: biggest,
|
|
tag: Tag::unknown(),
|
|
})
|
|
}
|
|
|
|
pub fn min(data: Vec<Value>) -> Result<Value, ShellError> {
|
|
let mut smallest = data
|
|
.first()
|
|
.ok_or_else(|| ShellError::unexpected(ERR_EMPTY_DATA))?
|
|
.value
|
|
.clone();
|
|
|
|
for value in data.iter() {
|
|
if let Ok(greater_than) = compare_values(Operator::LessThan, &value.value, &smallest) {
|
|
if greater_than {
|
|
smallest = value.value.clone();
|
|
}
|
|
} else {
|
|
return Err(ShellError::unexpected(format!(
|
|
"Could not compare\nleft: {:?}\nright: {:?}",
|
|
smallest, value.value
|
|
)));
|
|
}
|
|
}
|
|
Ok(Value {
|
|
value: smallest,
|
|
tag: Tag::unknown(),
|
|
})
|
|
}
|
|
|
|
pub fn product(data: Vec<Value>) -> Result<Value, ShellError> {
|
|
if data.is_empty() {
|
|
return Err(ShellError::unexpected(ERR_EMPTY_DATA));
|
|
}
|
|
|
|
let mut prod = UntaggedValue::int(1).into_untagged_value();
|
|
for value in data {
|
|
match value.value {
|
|
UntaggedValue::Primitive(_) => {
|
|
prod = match compute_values(Operator::Multiply, &prod, &value) {
|
|
Ok(v) => v.into_untagged_value(),
|
|
Err((left_type, right_type)) => {
|
|
return Err(ShellError::coerce_error(
|
|
left_type.spanned_unknown(),
|
|
right_type.spanned_unknown(),
|
|
))
|
|
}
|
|
};
|
|
}
|
|
_ => {
|
|
return Err(ShellError::labeled_error(
|
|
"Attempted to compute the product of a value that cannot be multiplied.",
|
|
"value appears here",
|
|
value.tag.span,
|
|
))
|
|
}
|
|
}
|
|
}
|
|
Ok(prod)
|
|
}
|