use super::{Field, FieldType}; use crate::{ mtterror::{ErrorID, MTTError}, name::NameType, }; use chrono::{DateTime, Utc}; use std::time::Duration; use uuid::Uuid; #[derive(Clone, Debug)] pub enum Operand { Add, Equal, GreaterThan, GreaterThanEqual, LessThan, LessThanEqual, } impl Operand { pub fn validate(&self, x: &Field, y: &Field) -> bool { match self { Self::Equal => x == y, Self::GreaterThan => x > y, Self::GreaterThanEqual => x >= y, Self::LessThan => x < y, Self::LessThanEqual => x <= y, _ => false, } } } #[cfg(test)] mod operands { use super::*; #[test] fn equals_true() { let data: Field = Uuid::new_v4().into(); assert!(Operand::Equal.validate(&data, &data)); } #[test] fn equals_false() { let x: Field = Uuid::new_v4().into(); let mut y: Field = Uuid::new_v4().into(); while x == y { y = Uuid::new_v4().into(); } assert!(!Operand::Equal.validate(&x, &y)); } #[test] fn does_greater() { let data: Vec = vec![1.into(), 2.into(), 3.into()]; assert!(!Operand::GreaterThan.validate(&data[0], &data[1])); assert!(!Operand::GreaterThan.validate(&data[1], &data[1])); assert!(Operand::GreaterThan.validate(&data[2], &data[1])); } #[test] fn does_greater_equal() { let data: Vec = vec![1.into(), 2.into(), 3.into()]; assert!(!Operand::GreaterThanEqual.validate(&data[0], &data[1])); assert!(Operand::GreaterThanEqual.validate(&data[1], &data[1])); assert!(Operand::GreaterThanEqual.validate(&data[2], &data[1])); } #[test] fn does_lesser() { let data: Vec = vec![1.into(), 2.into(), 3.into()]; assert!(Operand::LessThan.validate(&data[0], &data[1])); assert!(!Operand::LessThan.validate(&data[1], &data[1])); assert!(!Operand::LessThan.validate(&data[2], &data[1])); } #[test] fn does_lesser_equal() { let data: Vec = vec![1.into(), 2.into(), 3.into()]; assert!(Operand::LessThanEqual.validate(&data[0], &data[1])); assert!(Operand::LessThanEqual.validate(&data[1], &data[1])); assert!(!Operand::LessThanEqual.validate(&data[2], &data[1])); } } #[derive(Clone, Debug)] pub enum CalcValue { Calculate(Calculation), Existing(FieldType), FType(FieldType), None, Value(Field), } impl CalcValue { pub fn get(&self, existing: &Field) -> Field { match self { Self::Calculate(calc) => calc.calculate(existing), Self::Existing(_) => existing.clone(), Self::FType(ftype) => ftype.get_default(), Self::None => Field::None, Self::Value(field) => field.clone(), } } pub fn get_type(&self) -> FieldType { match self { Self::Calculate(calc) => calc.get_type(), Self::Existing(ftype) => ftype.clone(), Self::FType(ftype) => ftype.clone(), Self::None => FieldType::None, Self::Value(field) => field.into(), } } } impl From for CalcValue { fn from(value: Calculation) -> Self { Self::Calculate(value) } } impl From for CalcValue { fn from(value: Field) -> Self { Self::Value(value) } } impl From<&Field> for CalcValue { fn from(value: &Field) -> Self { Self::from(value.clone()) } } impl From for CalcValue { fn from(value: FieldType) -> Self { Self::FType(value) } } impl From for CalcValue { fn from(value: bool) -> Self { let output: Field = value.into(); Self::from(output).into() } } impl From> for CalcValue { fn from(value: DateTime) -> Self { let output: Field = value.into(); Self::from(output).into() } } impl From for CalcValue { fn from(value: Duration) -> Self { let output: Field = value.into(); Self::from(output).into() } } impl From for CalcValue { fn from(value: i128) -> Self { let output: Field = value.into(); Self::from(output).into() } } impl From<&str> for CalcValue { fn from(value: &str) -> Self { let output: Field = value.into(); Self::from(output).into() } } impl From for CalcValue { fn from(value: String) -> Self { let output: Field = value.into(); Self::from(output).into() } } impl From for CalcValue { fn from(value: Uuid) -> Self { let output: Field = value.into(); Self::from(output).into() } } #[cfg(test)] mod calcvalues { use super::*; #[test] fn from_uuid() { let value = Uuid::new_v4(); let expected: Field = value.into(); let result: CalcValue = value.into(); match result.clone() { CalcValue::Value(data) => assert_eq!(data, expected), _ => unreachable!("got {:?}, should have gotten a field", result), } assert_eq!(result.get(&Field::None), expected); } #[test] fn from_str() { let value = "something"; let expected: Field = value.into(); let result: CalcValue = value.into(); match result.clone() { CalcValue::Value(data) => assert_eq!(data, expected), _ => unreachable!("got {:?}, should have gotten a field", result), } assert_eq!(result.get(&Field::None), expected); } #[test] fn from_string() { let value = "data".to_string(); let expected: Field = value.clone().into(); let result: CalcValue = value.into(); match result.clone() { CalcValue::Value(data) => assert_eq!(data, expected), _ => unreachable!("got {:?}, should have gotten a field", result), } assert_eq!(result.get(&Field::None), expected); } #[test] fn from_boolean() { let value = true; let expected: Field = value.clone().into(); let result: CalcValue = value.into(); match result.clone() { CalcValue::Value(data) => assert_eq!(data, expected), _ => unreachable!("got {:?}, should have gotten a field", result), } assert_eq!(result.get(&Field::None), expected); } #[test] fn from_datetime() { let value = Utc::now(); let expected: Field = value.clone().into(); let result: CalcValue = value.into(); match result.clone() { CalcValue::Value(data) => assert_eq!(data, expected), _ => unreachable!("got {:?}, should have gotten a field", result), } assert_eq!(result.get(&Field::None), expected); } #[test] fn from_duration() { let value = Duration::from_secs(5); let expected: Field = value.clone().into(); let result: CalcValue = value.into(); match result.clone() { CalcValue::Value(data) => assert_eq!(data, expected), _ => unreachable!("got {:?}, should have gotten a field", result), } assert_eq!(result.get(&Field::None), expected); } #[test] fn from_integer() { let value: i128 = 5; let expected: Field = value.clone().into(); let result: CalcValue = value.into(); match result.clone() { CalcValue::Value(data) => assert_eq!(data, expected), _ => unreachable!("got {:?}, should have gotten a field", result), } assert_eq!(result.get(&Field::None), expected); } #[test] fn from_calculation() { let duration = Duration::from_secs(300); let start = Utc::now() + duration; let mut calc = Calculation::new(Operand::Add); calc.add_value(FieldType::DateTime).unwrap(); calc.add_value(duration.clone()).unwrap(); let result: CalcValue = calc.into(); let data = match result.get(&Field::None) { Field::DateTime(data) => data, _ => unreachable!(), }; let stop = Utc::now() + duration; assert!( data > start && data < stop, "should be about 5 minutes ahead" ); } } #[derive(Clone, Debug)] pub struct Calculation { operation: Operand, values: Vec, } impl Calculation { pub fn new(operand: Operand) -> Self { Self { operation: operand, values: Vec::new(), } } #[allow(dead_code)] fn operation(&self) -> &Operand { &self.operation } #[allow(dead_code)] fn get_fields(&self, existing: Field) -> Vec { let mut output = Vec::new(); for item in self.values.iter() { output.push(item.get(&existing)); } output } pub fn get_type(&self) -> FieldType { if self.values.is_empty() { FieldType::None } else { self.values[0].get_type() } } pub fn add_value(&mut self, data: CV) -> Result<(), MTTError> where CV: Into, { let holder: CalcValue = data.into(); if self.values.is_empty() { self.values.push(holder); return Ok(()); } let mut base = self.get_type(); match self.operation { Operand::Add => match base { FieldType::DateTime => base = FieldType::Duration, _ => {} }, _ => {} } let ftype = holder.get_type(); if base == ftype { self.values.push(holder); } else { let err = MTTError::new(NameType::None, ErrorID::FieldInvalidType); return Err(err); } Ok(()) } #[allow(dead_code)] fn validate_value(&self, value: CV) -> Result<(), MTTError> where CV: Into, { if self.values.is_empty() { return Ok(()); } let holder = value.into(); let mut base = self.get_type(); match self.operation { Operand::Add => { if base == FieldType::DateTime { base = FieldType::Duration; } } _ => {} } let ftype = holder.get_type(); if base == ftype { Ok(()) } else { let err = MTTError::new(NameType::None, ErrorID::FieldInvalidType); return Err(err); } } pub fn calculate(&self, existing: &Field) -> Field { let mut result = Field::None; match self.operation { Operand::Add => { let mut first = true; for value in self.values.iter() { let data = value.get(existing); if first { result = data; first = false; } else { result += data; } } } Operand::Equal => { if self.values.len() >= 2 { result = self.values[0] .get(existing) .equal(&self.values[1].get(existing)); } } Operand::GreaterThan => { if self.values.len() >= 2 { result = self.values[0] .get(existing) .greater(&self.values[1].get(existing)); } } Operand::GreaterThanEqual => { if self.values.len() >= 2 { result = self.values[0] .get(existing) .greater_equal(&self.values[1].get(existing)); } } Operand::LessThan => { if self.values.len() >= 2 { result = self.values[0] .get(existing) .lesser(&self.values[1].get(existing)); } } Operand::LessThanEqual => { if self.values.len() >= 2 { result = self.values[0] .get(existing) .lesser_equal(&self.values[1].get(existing)); } } } result } } #[cfg(test)] mod calculations { use super::*; use rand::random; #[test] fn errors_on_different_field_types() { let mut calc = Calculation::new(Operand::Equal); calc.add_value(Uuid::nil()).unwrap(); match calc.add_value("other") { Ok(_) => unreachable!("should have errored with wrong type"), Err(err) => { let err_id = err.error_id(); match err_id { ErrorID::FieldInvalidType => {} _ => unreachable!("got {:?}, expected wrong field type", err_id), } } } } #[test] fn returns_reference_to_operand() { let calc = Calculation::new(Operand::Add); match calc.operation() { Operand::Add => {} _ => unreachable!("got {:?}, shold have gotten assign", calc.operation()), } let calc = Calculation::new(Operand::Equal); match calc.operation() { Operand::Equal => {} _ => unreachable!("got {:?}, shold have gotten assign", calc.operation()), } } #[test] fn can_equal_true() { let mut calc = Calculation::new(Operand::Equal); let data: Field = Uuid::new_v4().into(); calc.add_value(data.clone()).unwrap(); calc.add_value(data.clone()).unwrap(); let expected: Field = true.into(); let result = calc.calculate(&Field::None); assert_eq!(result, expected); } #[test] fn can_equal_false() { let mut calc = Calculation::new(Operand::Equal); let value1: Field = "fred".into(); let value2: Field = "barney".into(); calc.add_value(value1).unwrap(); calc.add_value(value2).unwrap(); let expected: Field = false.into(); let result = calc.calculate(&Field::None); assert_eq!(result, expected); } #[test] fn can_greater_than() { let data: Vec<(Field, Field)> = vec![ (0.into(), false.into()), (1.into(), false.into()), (2.into(), true.into()), ]; for (item, expected) in data.iter() { let mut calc = Calculation::new(Operand::GreaterThan); calc.add_value(item.clone()).unwrap(); calc.add_value(data[1].0.clone()).unwrap(); let result = calc.calculate(&Field::None); assert_eq!(&result, expected); } } #[test] fn can_greater_than_equal() { let data: Vec<(Field, Field)> = vec![ (0.into(), false.into()), (1.into(), true.into()), (2.into(), true.into()), ]; for (item, expected) in data.iter() { let mut calc = Calculation::new(Operand::GreaterThanEqual); calc.add_value(item.clone()).unwrap(); calc.add_value(data[1].0.clone()).unwrap(); let result = calc.calculate(&Field::None); assert_eq!(&result, expected); } } #[test] fn can_lesser_than() { let data: Vec<(Field, Field)> = vec![ (0.into(), true.into()), (1.into(), false.into()), (2.into(), false.into()), ]; for (item, expected) in data.iter() { let mut calc = Calculation::new(Operand::LessThan); calc.add_value(item.clone()).unwrap(); calc.add_value(data[1].0.clone()).unwrap(); let result = calc.calculate(&Field::None); assert_eq!(&result, expected); } } #[test] fn can_lesser_than_equal() { let data: Vec<(Field, Field)> = vec![ (0.into(), true.into()), (1.into(), true.into()), (2.into(), false.into()), ]; for (item, expected) in data.iter() { let mut calc = Calculation::new(Operand::LessThanEqual); calc.add_value(item.clone()).unwrap(); calc.add_value(data[1].0.clone()).unwrap(); let result = calc.calculate(&Field::None); assert_eq!(&result, expected); } } #[test] fn can_add_numbers() { let mut calc = Calculation::new(Operand::Add); let value1: i128 = random::().into(); let value2: i128 = random::().into(); let expected: Field = { value1 + value2 }.into(); let value1: Field = value1.into(); let value2: Field = value2.into(); calc.add_value(value1.clone()).unwrap(); calc.add_value(value2.clone()).unwrap(); let result = calc.calculate(&Field::None); assert_eq!( result, expected, "{:?} plus {:?} should equal {:?}", value1, value2, expected ); } #[test] fn can_use_existing_values() { let mut calc = Calculation::new(Operand::Add); let value1: i128 = random::().into(); let value2: i128 = random::().into(); let expected: Field = { value1 + value2 }.into(); let value1: Field = value1.into(); let value2: Field = value2.into(); calc.add_value(value1.clone()).unwrap(); calc.add_value(CalcValue::Existing(FieldType::Integer)) .unwrap(); let result = calc.calculate(&value2); assert_eq!( result, expected, "{:?} plus {:?} should equal {:?}", value1, value2, expected ); } #[test] fn returns_error_on_mismatch() { let mut calc = Calculation::new(Operand::Add); calc.add_value(Uuid::nil()).unwrap(); match calc.add_value("mismatch") { Ok(_) => unreachable!("should have returned an error"), Err(err) => match err.error_id() { ErrorID::FieldInvalidType => {} _ => unreachable!("got {:?}, expected wrong field type", err), }, } } #[test] fn datetime_accepts_duration() { let mut calc = Calculation::new(Operand::Add); let duration = Duration::from_secs(3600); let start = Utc::now() + duration; calc.add_value(FieldType::DateTime).unwrap(); match calc.add_value(duration.clone()) { Ok(_) => {} Err(err) => unreachable!("got {:?}, should have returned normally", err), } let result = calc.calculate(&Field::None); let stop = Utc::now() + duration; match result { Field::DateTime(data) => { assert!(data > start); assert!(data < stop); } _ => unreachable!("got {:?}, should have been datetime", result), } } }