aboutsummaryrefslogtreecommitdiff
path: root/karld/src/condition.rs
diff options
context:
space:
mode:
authormetamuffin <yvchraiqi@protonmail.com>2022-06-10 10:47:16 +0200
committermetamuffin <yvchraiqi@protonmail.com>2022-06-10 10:47:16 +0200
commit3238f8517097745032e19b3e26f57f0465a00b28 (patch)
tree2c712d2ab45276bed2981dbc32b7a4adeadbc878 /karld/src/condition.rs
parent829f0dc5ac68ee8a030894ce26c83b1c4eb02104 (diff)
downloadkarlender-3238f8517097745032e19b3e26f57f0465a00b28.tar
karlender-3238f8517097745032e19b3e26f57f0465a00b28.tar.bz2
karlender-3238f8517097745032e19b3e26f57f0465a00b28.tar.zst
move to workspace
Diffstat (limited to 'karld/src/condition.rs')
-rw-r--r--karld/src/condition.rs364
1 files changed, 364 insertions, 0 deletions
diff --git a/karld/src/condition.rs b/karld/src/condition.rs
new file mode 100644
index 0000000..830e8ae
--- /dev/null
+++ b/karld/src/condition.rs
@@ -0,0 +1,364 @@
+use chrono::{Datelike, Duration, NaiveDate, NaiveDateTime, NaiveTime, Timelike};
+use serde::{Deserialize, Serialize};
+use std::cmp::{max, min};
+use Direction::*;
+use Edge::*;
+
+#[derive(Debug, Clone, Serialize, Deserialize)]
+#[serde(rename_all = "snake_case")]
+pub enum Condition {
+ From(Box<Condition>),
+
+ Or(Vec<Condition>),
+ And(Vec<Condition>),
+ Invert(Box<Condition>),
+
+ Equal {
+ prop: Property,
+ value: i64,
+ modulus: Option<i64>,
+ },
+ Range {
+ prop: Property,
+ min: i64,
+ max: i64,
+ modulus: Option<i64>,
+ },
+}
+
+#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
+#[serde(rename_all = "snake_case")]
+pub enum Property {
+ Year,
+ Monthofyear,
+ Weekofmonth,
+ Dayofyear,
+ Dayofmonth,
+ Dayofweek,
+ Hour,
+ Minute,
+ Second,
+ Unix,
+}
+
+#[derive(Debug, Clone, Serialize, Deserialize)]
+pub struct Range<T>(T, T);
+impl<T: PartialOrd + PartialEq> Range<T> {
+ pub fn includes(&self, a: T) -> bool {
+ a > self.0 && a < self.1
+ }
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub enum Edge {
+ Start,
+ End,
+}
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub enum Direction {
+ Forward,
+ Backward,
+}
+
+impl Condition {
+ pub fn find(
+ &self,
+ edge: Edge,
+ dir: Direction,
+ mut from: NaiveDateTime,
+ ) -> Option<NaiveDateTime> {
+ match self {
+ Condition::And(cs) => loop {
+ // TODO improve efficiency for backward search
+ let last_start = cs
+ .iter()
+ .map(|c| c.find(Start, dir, from))
+ .reduce(|a, b| Some(max(a?, b?)))?;
+ let first_end = cs
+ .iter()
+ .map(|c| c.find(End, dir, from))
+ .reduce(|a, b| Some(min(a?, b?)))?;
+ match last_start {
+ Some(start) => match first_end {
+ Some(end) => {
+ if end > start {
+ break Some(match edge {
+ Start => start,
+ End => end,
+ });
+ } else {
+ from = start - Duration::seconds(10); // TODO proper fix
+ }
+ }
+ None => break Some(start),
+ },
+ None => break None,
+ }
+ },
+ 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 value = *value;
+ let off: i64 = match edge {
+ Start => 0,
+ End => 1,
+ };
+ let dir_off = match dir {
+ Forward => 0,
+ Backward => -1,
+ };
+ match prop {
+ Property::Year => {
+ let geq = match dir {
+ Forward => |a, b| a >= b,
+ Backward => |a, b| a < b,
+ };
+
+ if geq(from.year(), (value + off) as i32) {
+ None
+ } else {
+ Some(NaiveDateTime::new(
+ NaiveDate::from_ymd((value + off) as i32, 1, 1),
+ NaiveTime::from_hms(0, 0, 0),
+ ))
+ }
+ }
+ Property::Monthofyear => {
+ let rollover = (value + off) / 12;
+ let value = (value + off) % 12;
+ // month still coming for this year
+ if from.month0() < value as u32 {
+ Some(NaiveDateTime::new(
+ NaiveDate::from_ymd(
+ from.year() + (rollover + dir_off) as i32,
+ value as u32 + 1,
+ 1,
+ ),
+ NaiveTime::from_hms(0, 0, 0),
+ ))
+ } else {
+ Some(NaiveDateTime::new(
+ NaiveDate::from_ymd(
+ from.year() + (rollover + dir_off + 1) as i32,
+ value as u32 + 1,
+ 1,
+ ),
+ NaiveTime::from_hms(0, 0, 0),
+ ))
+ }
+ }
+ Property::Weekofmonth => todo!(),
+ Property::Dayofyear => {
+ todo!()
+ }
+ Property::Dayofmonth => {
+ // let mut target = NaiveDateTime::new(
+ // NaiveDate::from_ymd(from.year(), from.month(), value as u32),
+ // NaiveTime::from_hms(0, 0, 0),
+ // );
+ // if edge == End {
+ // target += Duration::days(1)
+ // }
+ // fn increment_month(d: NaiveDateTime) -> NaiveDateTime {
+ // NaiveDateTime::new(
+ // NaiveDate::from_ymd(
+ // d.year() + (d.month() as i32 / 12),
+ // (d.month() + 1) % 12,
+ // d.day(),
+ // ),
+ // NaiveTime::from_hms(d.hour(), d.minute(), d.second()),
+ // )
+ // }
+ // let dir_off = match dir {
+ // Forward => |d| d,
+ // Backward => increment_month,
+ // };
+ // if target > from {
+ // Some(dir_off(target))
+ // } else {
+ // Some(increment_month(dir_off(target)))
+ // }
+ todo!()
+ }
+ Property::Dayofweek => todo!(),
+ Property::Hour => {
+ let mut target = NaiveDateTime::new(
+ NaiveDate::from_ymd(from.year(), from.month(), from.day()),
+ NaiveTime::from_hms(value as u32, 0, 0),
+ );
+ if edge == End {
+ target += Duration::hours(1)
+ }
+ let dir_off = match dir {
+ Forward => Duration::zero(),
+ Backward => Duration::days(-1),
+ };
+ if target > from {
+ Some(target + dir_off)
+ } else {
+ Some(target + dir_off + Duration::days(1))
+ }
+ }
+ Property::Minute => {
+ let mut target = NaiveDateTime::new(
+ NaiveDate::from_ymd(from.year(), from.month(), from.day()),
+ NaiveTime::from_hms(from.hour(), value as u32, 0),
+ );
+ if edge == End {
+ target += Duration::minutes(1)
+ }
+ let dir_off = match dir {
+ Forward => Duration::zero(),
+ Backward => Duration::hours(-1),
+ };
+ if target > from {
+ Some(target + dir_off)
+ } else {
+ Some(target + dir_off + Duration::hours(1))
+ }
+ }
+ Property::Second => {
+ let mut target = NaiveDateTime::new(
+ NaiveDate::from_ymd(from.year(), from.month(), from.day()),
+ NaiveTime::from_hms(from.hour(), from.minute(), value as u32),
+ );
+ if edge == End {
+ target += Duration::seconds(1)
+ }
+ let dir_off = match dir {
+ Forward => Duration::zero(),
+ Backward => Duration::minutes(-1),
+ };
+ if target > from {
+ Some(target + dir_off)
+ } else {
+ Some(target + dir_off + Duration::minutes(1))
+ }
+ }
+ Property::Unix => {
+ let geq = match dir {
+ Forward => |a, b| a >= b,
+ Backward => |a, b| a < b,
+ };
+ if geq(from.timestamp(), (value + off) as i64) {
+ None
+ } else {
+ Some(NaiveDateTime::from_timestamp(value, 0))
+ }
+ }
+ }
+ }
+ 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, Utc};
+ use std::str::FromStr;
+ use Direction::*;
+ use Edge::*;
+
+ #[test]
+ fn blub() {
+ let cond = Condition::And(vec![
+ Condition::Equal {
+ modulus: None,
+ prop: Property::Monthofyear,
+ value: 1,
+ },
+ Condition::Equal {
+ modulus: None,
+ prop: Property::Hour,
+ value: 12,
+ },
+ ]);
+ // let cond = Condition::Equal {
+ // modulus: None,
+ // prop: Property::Hour,
+ // value: 12,
+ // };
+ let dt = Utc::now().naive_utc();
+ println!("START FORWARD => {:?}", cond.find(Start, Forward, dt));
+ println!("END FORWARD => {:?}", cond.find(End, Forward, dt));
+ println!("START BACKWARD => {:?}", cond.find(Start, Backward, dt));
+ println!("END BACKWARD => {:?}", cond.find(End, Backward, dt));
+ }
+
+ #[test]
+ fn year_equal() {
+ 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);
+ }
+
+ #[test]
+ fn month_equal() {
+ let cond = Condition::Equal {
+ modulus: None,
+ prop: Property::Monthofyear,
+ value: 3,
+ };
+
+ let dt = NaiveDateTime::from_str("2022-06-07T13:37:48").unwrap();
+
+ assert_eq!(
+ cond.find(Edge::Start, Direction::Forward, dt),
+ Some(NaiveDateTime::from_str("2023-04-01T00:00:00").unwrap())
+ );
+ assert_eq!(
+ cond.find(Edge::End, Direction::Forward, dt),
+ Some(NaiveDateTime::from_str("2023-05-01T00:00:00").unwrap())
+ );
+ assert_eq!(
+ cond.find(Edge::Start, Direction::Backward, dt),
+ Some(NaiveDateTime::from_str("2022-04-01T00:00:00").unwrap())
+ );
+ assert_eq!(
+ cond.find(Edge::End, Direction::Backward, dt),
+ Some(NaiveDateTime::from_str("2022-05-01T00:00:00").unwrap())
+ );
+ }
+}