use std::{
ops::{Add, Div, Mul, Sub},
time::Duration,
};
use anyhow::{bail, Result};
use chrono::naive::NaiveTime;
use crate::{
calculate::{Arithmetic, Calculateable},
compare::{Compareable, Operator},
};
#[derive(Debug, PartialEq, PartialOrd, Clone)]
pub enum Value {
String(String),
Numeric(f64),
Bool(bool),
Time(NaiveTime),
Duration(Duration),
}
impl Compareable for Value {
fn compare(&self, rhs: &Self, operator: Operator) -> bool {
match operator {
Operator::Equal => self == rhs,
Operator::NotEqual => self != rhs,
Operator::Greater => self > rhs,
Operator::Less => self < rhs,
Operator::GreaterEqual => self >= rhs,
Operator::LessEqual => self <= rhs,
}
}
}
impl Calculateable for Value {
fn calculate(self, rhs: &Self, operator: Arithmetic) -> Result<Self> {
match operator {
Arithmetic::Add => self + rhs,
Arithmetic::Sub => self - rhs,
Arithmetic::Mul => self * rhs,
Arithmetic::Div => self / rhs,
}
}
}
impl Add<&Self> for Value {
type Output = Result<Value>;
fn add(self, rhs: &Self) -> Self::Output {
match (self, rhs) {
(Value::String(lhs), Value::String(rhs)) => Ok(Value::String(lhs + &rhs)),
(Value::String(lhs), Value::Numeric(rhs)) => {
Ok(Value::String(format!("{} {}", lhs, rhs)))
}
(Value::Numeric(lhs), Value::Numeric(rhs)) => Ok(Value::Numeric(lhs + rhs)),
(Value::Duration(lhs), Value::Duration(rhs)) => Ok(Value::Duration(lhs + *rhs)),
(Value::Time(lhs), Value::Duration(rhs)) => Ok(Value::Time(
lhs + chrono::Duration::from_std(rhs.clone()).expect("Unable to convert duration"),
)),
_ => bail!("Incompatible types for addition"),
}
}
}
impl Sub<&Self> for Value {
type Output = Result<Value>;
fn sub(self, rhs: &Self) -> Self::Output {
match (self, rhs) {
(Value::Numeric(lhs), Value::Numeric(rhs)) => Ok(Value::Numeric(lhs - rhs)),
(Value::Duration(lhs), Value::Duration(rhs)) => Ok(Value::Duration(lhs - *rhs)),
(Value::Time(lhs), Value::Duration(rhs)) => Ok(Value::Time(
lhs - chrono::Duration::from_std(rhs.clone()).expect("Unable to convert duration"),
)),
_ => bail!("Incompatible types for substraction"),
}
}
}
impl Mul<&Self> for Value {
type Output = Result<Value>;
fn mul(self, rhs: &Self) -> Self::Output {
match (self, rhs) {
(Value::Numeric(lhs), Value::Numeric(rhs)) => Ok(Value::Numeric(lhs * rhs)),
(Value::Duration(lhs), Value::Numeric(rhs)) => {
Ok(Value::Duration(lhs * rhs.round() as u32))
}
_ => bail!("Incompatible types for multiiplication"),
}
}
}
impl Div<&Self> for Value {
type Output = Result<Value>;
fn div(self, rhs: &Self) -> Self::Output {
match (self, rhs) {
(Value::Numeric(lhs), Value::Numeric(rhs)) => Ok(Value::Numeric(lhs / rhs)),
(Value::Duration(lhs), Value::Numeric(rhs)) => {
Ok(Value::Duration(lhs / rhs.round() as u32))
}
_ => bail!("Incompatible types for division"),
}
}
}
macro_rules! impl_value {
($from:ty, $to:expr) => {
impl From<$from> for Value {
fn from(value: $from) -> Self {
$to(value.into())
}
}
};
}
impl TryFrom<chrono::Duration> for Value {
type Error = anyhow::Error;
fn try_from(value: chrono::Duration) -> Result<Self, Self::Error> {
Ok(value.to_std()?.into())
}
}
impl_value!(&str, Value::String);
impl_value!(String, Value::String);
impl_value!(i8, Value::Numeric);
impl_value!(u8, Value::Numeric);
impl_value!(i16, Value::Numeric);
impl_value!(u16, Value::Numeric);
impl_value!(i32, Value::Numeric);
impl_value!(u32, Value::Numeric);
impl_value!(f32, Value::Numeric);
impl_value!(f64, Value::Numeric);
impl_value!(bool, Value::Bool);
impl_value!(NaiveTime, Value::Time);
impl_value!(Duration, Value::Duration);