Merge pull request #266 from luccasmmg/engine-q-math-2

Added math and min commands
This commit is contained in:
JT 2021-11-01 06:45:16 +13:00 committed by GitHub
commit 80a4a5eb28
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 181 additions and 6 deletions

View File

@ -53,6 +53,8 @@ pub fn create_default_context() -> EngineState {
Math, Math,
MathAbs, MathAbs,
MathAvg, MathAvg,
MathMax,
MathMin,
Mkdir, Mkdir,
Module, Module,
Mv, Mv,

View File

@ -50,6 +50,7 @@ pub fn average(values: &[Value], head: &Span) -> Result<Value, ShellError> {
span: Span::unknown(), span: Span::unknown(),
}, },
values.to_vec(), values.to_vec(),
*head,
)?; )?;
match total { match total {
Value::Filesize { val, span } => Ok(Value::Filesize { Value::Filesize { val, span } => Ok(Value::Filesize {

View File

@ -0,0 +1,57 @@
use crate::math::reducers::{reducer_for, Reduce};
use crate::math::utils::run_with_function;
use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{Example, PipelineData, ShellError, Signature, Span, Value};
#[derive(Clone)]
pub struct SubCommand;
impl Command for SubCommand {
fn name(&self) -> &str {
"math max"
}
fn signature(&self) -> Signature {
Signature::build("math max")
}
fn usage(&self) -> &str {
"Finds the maximum within a list of numbers or tables"
}
fn run(
&self,
_engine_state: &EngineState,
_stack: &mut Stack,
call: &Call,
input: PipelineData,
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
run_with_function(call, input, maximum)
}
fn examples(&self) -> Vec<Example> {
vec![Example {
description: "Find the maximum of list of numbers",
example: "[-50 100 25] | math max",
result: Some(Value::test_int(100)),
}]
}
}
pub fn maximum(values: &[Value], head: &Span) -> Result<Value, ShellError> {
let max_func = reducer_for(Reduce::Maximum);
max_func(Value::nothing(), values.to_vec(), *head)
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_examples() {
use crate::test_examples;
test_examples(SubCommand {})
}
}

View File

@ -0,0 +1,57 @@
use crate::math::reducers::{reducer_for, Reduce};
use crate::math::utils::run_with_function;
use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{Example, PipelineData, ShellError, Signature, Span, Value};
#[derive(Clone)]
pub struct SubCommand;
impl Command for SubCommand {
fn name(&self) -> &str {
"math min"
}
fn signature(&self) -> Signature {
Signature::build("math min")
}
fn usage(&self) -> &str {
"Finds the minimum within a list of numbers or tables"
}
fn run(
&self,
_engine_state: &EngineState,
_stack: &mut Stack,
call: &Call,
input: PipelineData,
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
run_with_function(call, input, minimum)
}
fn examples(&self) -> Vec<Example> {
vec![Example {
description: "Get the minimum of a list of numbers",
example: "[-50 100 25] | math min",
result: Some(Value::test_int(-50)),
}]
}
}
pub fn minimum(values: &[Value], head: &Span) -> Result<Value, ShellError> {
let min_func = reducer_for(Reduce::Minimum);
min_func(Value::nothing(), values.to_vec(), *head)
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_examples() {
use crate::test_examples;
test_examples(SubCommand {})
}
}

View File

@ -1,9 +1,13 @@
mod abs; mod abs;
mod avg; mod avg;
pub mod command; pub mod command;
mod max;
mod min;
mod reducers; mod reducers;
mod utils; mod utils;
pub use abs::SubCommand as MathAbs; pub use abs::SubCommand as MathAbs;
pub use avg::SubCommand as MathAvg; pub use avg::SubCommand as MathAvg;
pub use command::MathCommand as Math; pub use command::MathCommand as Math;
pub use max::SubCommand as MathMax;
pub use min::SubCommand as MathMin;

View File

@ -1,19 +1,73 @@
use nu_protocol::{ShellError, Span, Value}; use nu_protocol::{ShellError, Span, Value};
use std::cmp::Ordering;
#[allow(dead_code)] #[allow(dead_code)]
pub enum Reduce { pub enum Reduce {
Summation, Summation,
Minimum,
Maximum,
} }
pub fn reducer_for( pub type ReducerFunction =
command: Reduce, Box<dyn Fn(Value, Vec<Value>, Span) -> Result<Value, ShellError> + Send + Sync + 'static>;
) -> Box<dyn Fn(Value, Vec<Value>) -> Result<Value, ShellError> + Send + Sync + 'static> {
pub fn reducer_for(command: Reduce) -> ReducerFunction {
match command { match command {
Reduce::Summation => Box::new(|_, values| sum(values)), Reduce::Summation => Box::new(|_, values, head| sum(values, head)),
Reduce::Minimum => Box::new(|_, values, head| min(values, head)),
Reduce::Maximum => Box::new(|_, values, head| max(values, head)),
} }
} }
pub fn sum(data: Vec<Value>) -> Result<Value, ShellError> { pub fn max(data: Vec<Value>, head: Span) -> Result<Value, ShellError> {
let mut biggest = data
.first()
.ok_or_else(|| ShellError::UnsupportedInput("Empty input".to_string(), Span::unknown()))?
.clone();
for value in &data {
if let Some(result) = value.partial_cmp(&biggest) {
if result == Ordering::Greater {
biggest = value.clone();
}
} else {
return Err(ShellError::OperatorMismatch {
op_span: head,
lhs_ty: biggest.get_type(),
lhs_span: biggest.span()?,
rhs_ty: value.get_type(),
rhs_span: value.span()?,
});
}
}
Ok(biggest)
}
pub fn min(data: Vec<Value>, head: Span) -> Result<Value, ShellError> {
let mut smallest = data
.first()
.ok_or_else(|| ShellError::UnsupportedInput("Empty input".to_string(), Span::unknown()))?
.clone();
for value in &data {
if let Some(result) = value.partial_cmp(&smallest) {
if result == Ordering::Less {
smallest = value.clone();
}
} else {
return Err(ShellError::OperatorMismatch {
op_span: head,
lhs_ty: smallest.get_type(),
lhs_span: smallest.span()?,
rhs_ty: value.get_type(),
rhs_span: value.span()?,
});
}
}
Ok(smallest)
}
pub fn sum(data: Vec<Value>, head: Span) -> Result<Value, ShellError> {
let initial_value = data.get(0); let initial_value = data.get(0);
let mut acc = match initial_value { let mut acc = match initial_value {
@ -42,7 +96,7 @@ pub fn sum(data: Vec<Value>) -> Result<Value, ShellError> {
| Value::Float { .. } | Value::Float { .. }
| Value::Filesize { .. } | Value::Filesize { .. }
| Value::Duration { .. } => { | Value::Duration { .. } => {
let new_value = acc.add(acc.span().unwrap_or_else(|_| Span::unknown()), value); let new_value = acc.add(head, value);
if new_value.is_err() { if new_value.is_err() {
return new_value; return new_value;
} }