use anyhow::anyhow;
use anyhow::Result;
use serde::{Deserialize, Serialize};
use serde_json::Value;
use crate::expression::Expression;
use crate::expression::FloatExpression;
use crate::expression::IntExpression;
#[derive(Deserialize, Serialize, Eq, PartialEq, Debug, Clone)]
pub enum ArithmeticOperator {
#[serde(rename = "+")]
Plus,
#[serde(rename = "-")]
Minus,
#[serde(rename = "*")]
Multiply,
#[serde(rename = "/")]
Divide,
#[serde(rename = "POW")]
Exponent,
#[serde(rename = "MOD")]
Modulo,
}
impl ArithmeticOperator {
pub fn evaluate(&self, a: &Value, b: &Value) -> Result<Expression> {
match self.evaluate_as_f64(a, b) {
Ok(value) => {
if value.is_finite() {
if value.floor() == value.ceil() {
Ok(Expression::Int(IntExpression::transient(value as i64)))
} else {
Ok(Expression::Float(FloatExpression::transient(value)))
}
} else {
Err(anyhow!(
"Result of arithmetic operator does not fit into an f64"
))
}
}
Err(error) => Err(error),
}
}
fn evaluate_as_f64(&self, a: &Value, b: &Value) -> Result<f64> {
match (a, b) {
(Value::Number(a), Value::Number(b)) => match (a.as_f64(), b.as_f64()) {
(Some(a), Some(b)) => match self {
ArithmeticOperator::Plus => Ok(a + b),
ArithmeticOperator::Minus => Ok(a - b),
ArithmeticOperator::Multiply => Ok(a * b),
ArithmeticOperator::Divide => Ok(a / b),
ArithmeticOperator::Exponent => Ok(f64::powf(a, b)),
ArithmeticOperator::Modulo => Ok(a % b),
},
(_, _) => Err(anyhow!(
"One of a or b does not fit into an f64 - a: {}, b: {}",
a,
b
)),
},
(_, _) => Err(anyhow!(
"Evaluated operands of arithmetic expression are not both numbers"
)),
}
}
}