use nu_protocol::{ShellError, Span, Value}; use std::cmp::Ordering; pub enum Reduce { Summation, Product, Minimum, Maximum, } pub type ReducerFunction = Box, Span, Span) -> Result + Send + Sync + 'static>; pub fn reducer_for(command: Reduce) -> ReducerFunction { match command { Reduce::Summation => Box::new(|_, values, span, head| sum(values, span, head)), Reduce::Product => Box::new(|_, values, span, head| product(values, span, head)), Reduce::Minimum => Box::new(|_, values, span, head| min(values, span, head)), Reduce::Maximum => Box::new(|_, values, span, head| max(values, span, head)), } } pub fn max(data: Vec, span: Span, head: Span) -> Result { let mut biggest = data .first() .ok_or_else(|| ShellError::UnsupportedInput { msg: "Empty input".to_string(), input: "value originates from here".into(), msg_span: head, input_span: span, })? .clone(); for value in &data { if value.partial_cmp(&biggest) == Some(Ordering::Greater) { biggest = value.clone(); } } Ok(biggest) } pub fn min(data: Vec, span: Span, head: Span) -> Result { let mut smallest = data .first() .ok_or_else(|| ShellError::UnsupportedInput { msg: "Empty input".to_string(), input: "value originates from here".into(), msg_span: head, input_span: span, })? .clone(); for value in &data { if value.partial_cmp(&smallest) == Some(Ordering::Less) { smallest = value.clone(); } } Ok(smallest) } pub fn sum(data: Vec, span: Span, head: Span) -> Result { let initial_value = data.first(); let mut acc = match initial_value { Some(v) => { let span = v.span(); match v { Value::Filesize { .. } => Ok(Value::filesize(0, span)), Value::Duration { .. } => Ok(Value::duration(0, span)), Value::Int { .. } | Value::Float { .. } => Ok(Value::int(0, span)), _ => Ok(Value::nothing(head)), } } None => Err(ShellError::UnsupportedInput { msg: "Empty input".to_string(), input: "value originates from here".into(), msg_span: head, input_span: span, }), }?; for value in &data { match value { Value::Int { .. } | Value::Float { .. } | Value::Filesize { .. } | Value::Duration { .. } => { acc = acc.add(head, value, head)?; } Value::Error { error, .. } => return Err(*error.clone()), other => { return Err(ShellError::UnsupportedInput { msg: format!("Attempted to compute the sum of a value '{}' that cannot be summed with a type of `{}`.", other.coerce_string()?, other.get_type()), input: "value originates from here".into(), msg_span: head, input_span: other.span(), }); } } } Ok(acc) } pub fn product(data: Vec, span: Span, head: Span) -> Result { let initial_value = data.first(); let mut acc = match initial_value { Some(v) => { let span = v.span(); match v { Value::Int { .. } | Value::Float { .. } => Ok(Value::int(1, span)), _ => Ok(Value::nothing(head)), } } None => Err(ShellError::UnsupportedInput { msg: "Empty input".to_string(), input: "value originates from here".into(), msg_span: head, input_span: span, }), }?; for value in &data { match value { Value::Int { .. } | Value::Float { .. } => { acc = acc.mul(head, value, head)?; } Value::Error { error, .. } => return Err(*error.clone()), other => { return Err(ShellError::UnsupportedInput { msg: format!("Attempted to compute the product of a value '{}' that cannot be multiplied with a type of `{}`.", other.coerce_string()?, other.get_type()), input: "value originates from here".into(), msg_span: head, input_span: other.span(), }); } } } Ok(acc) }