use std::cmp::{max, min}; use chrono::{Datelike, NaiveDate, NaiveDateTime, NaiveTime}; use serde::{Deserialize, Serialize}; use Direction::*; use Edge::*; #[derive(Debug, Clone, Serialize, Deserialize)] pub enum Condition { From(Box), Or(Vec), And(Vec), Invert(Box), Equal { prop: Property, value: i32, modulus: Option, }, Range { prop: Property, min: i32, max: i32, modulus: Option, }, } #[derive(Debug, Clone, Copy, Serialize, Deserialize)] pub enum Property { Year, Monthofyear, Weekofmonth, Dayofyear, Dayofmonth, Dayofweek, Hour, Minute, Second, Unix, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Range(T, T); impl Range { pub fn includes(&self, a: T) -> bool { a > self.0 && a < self.1 } } #[derive(Debug, Clone, Copy)] pub enum Edge { Start, End, } #[derive(Debug, Clone, Copy)] pub enum Direction { Forward, Backward, } impl Condition { pub fn find(&self, edge: Edge, dir: Direction, from: NaiveDateTime) -> Option { match self { Condition::And(_) => todo!(), Condition::Or(cs) => { cs.iter() .filter_map(|c| c.find(edge, dir, from)) .reduce(match dir { Forward => min, Backward => max, }) } Condition::From(c) => todo!(), Condition::Invert(c) => c.find(edge.invert(), dir, from), Condition::Equal { prop, value, modulus, } => { let geq = match dir { Forward => |a: i32, b: i32| a >= b, Backward => |a: i32, b: i32| a < b, }; let off = match edge { Start => 0, End => 1, }; match prop { Property::Year => { if geq(from.year(), *value + off) { None } else { Some(NaiveDateTime::new( NaiveDate::from_ymd(*value + off, 1, 1), NaiveTime::from_hms(0, 0, 0), )) } } Property::Monthofyear => todo!(), Property::Weekofmonth => todo!(), Property::Dayofyear => todo!(), Property::Dayofmonth => todo!(), Property::Dayofweek => todo!(), Property::Hour => todo!(), Property::Minute => todo!(), Property::Second => todo!(), Property::Unix => todo!(), } } Condition::Range { prop, min, max, modulus, } => todo!(), } } } impl Edge { pub fn invert(self) -> Self { match self { Edge::Start => Edge::End, Edge::End => Edge::Start, } } } #[cfg(test)] mod test { use super::{Condition, Direction, Edge, Property}; use chrono::NaiveDateTime; use std::str::FromStr; #[test] fn a() { let cond = Condition::Equal { modulus: None, prop: Property::Year, value: 2023, }; let dt = NaiveDateTime::from_str("2022-06-07T13:37:48").unwrap(); assert_eq!( cond.find(Edge::Start, Direction::Forward, dt).unwrap(), NaiveDateTime::from_str("2023-01-01T00:00:00").unwrap(), ); assert_eq!( cond.find(Edge::End, Direction::Forward, dt).unwrap(), NaiveDateTime::from_str("2024-01-01T00:00:00").unwrap(), ); assert_eq!(cond.find(Edge::End, Direction::Backward, dt), None); assert_eq!(cond.find(Edge::Start, Direction::Backward, dt), None); } }