use rust_decimal::Decimal;
use crate::ast::FunctionCall;
use crate::error::QueryError;
use super::super::Executor;
use super::super::types::{PostingContext, Value};
impl Executor<'_> {
pub(crate) fn eval_math_function(
&self,
name: &str,
func: &FunctionCall,
ctx: &PostingContext,
) -> Result<Value, QueryError> {
match name {
"ABS" => {
Self::require_args(name, func, 1)?;
let val = self.evaluate_expr(&func.args[0], ctx)?;
match val {
Value::Number(n) => Ok(Value::Number(n.abs())),
Value::Integer(i) => Ok(Value::Integer(i.abs())),
_ => Err(QueryError::Type("ABS expects a number".to_string())),
}
}
"NEG" => {
Self::require_args(name, func, 1)?;
let val = self.evaluate_expr(&func.args[0], ctx)?;
match val {
Value::Number(n) => Ok(Value::Number(-n)),
Value::Integer(i) => Ok(Value::Integer(-i)),
_ => Err(QueryError::Type("NEG expects a number".to_string())),
}
}
"ROUND" => self.eval_round(func, ctx),
"SAFEDIV" => self.eval_safediv(func, ctx),
_ => unreachable!(),
}
}
pub(crate) fn eval_round(
&self,
func: &FunctionCall,
ctx: &PostingContext,
) -> Result<Value, QueryError> {
if func.args.is_empty() || func.args.len() > 2 {
return Err(QueryError::InvalidArguments(
"ROUND".to_string(),
"expected 1 or 2 arguments".to_string(),
));
}
let val = self.evaluate_expr(&func.args[0], ctx)?;
let decimals = if func.args.len() == 2 {
match self.evaluate_expr(&func.args[1], ctx)? {
Value::Integer(i) => i as u32,
_ => {
return Err(QueryError::Type(
"ROUND second arg must be integer".to_string(),
));
}
}
} else {
0
};
match val {
Value::Number(n) => Ok(Value::Number(n.round_dp(decimals))),
Value::Integer(i) => Ok(Value::Integer(i)),
_ => Err(QueryError::Type("ROUND expects a number".to_string())),
}
}
pub(crate) fn eval_safediv(
&self,
func: &FunctionCall,
ctx: &PostingContext,
) -> Result<Value, QueryError> {
Self::require_args("SAFEDIV", func, 2)?;
let num = self.evaluate_expr(&func.args[0], ctx)?;
let den = self.evaluate_expr(&func.args[1], ctx)?;
match (num, den) {
(Value::Number(n), Value::Number(d)) => {
if d.is_zero() {
Ok(Value::Number(Decimal::ZERO))
} else {
Ok(Value::Number(n / d))
}
}
(Value::Integer(n), Value::Integer(d)) => {
if d == 0 {
Ok(Value::Integer(0))
} else {
Ok(Value::Integer(n / d))
}
}
_ => Err(QueryError::Type("SAFEDIV expects two numbers".to_string())),
}
}
}