diff options
Diffstat (limited to 'src/model')
| -rw-r--r-- | src/model/event.rs | 13 | ||||
| -rw-r--r-- | src/model/repetition.rs | 142 | 
2 files changed, 118 insertions, 37 deletions
| diff --git a/src/model/event.rs b/src/model/event.rs index 249d077..5e92692 100644 --- a/src/model/event.rs +++ b/src/model/event.rs @@ -17,17 +17,6 @@ pub struct Event {      pub repetition: Option<Repetition>,  } -pub fn init(date: NaiveDate) -> Event { -    Event { -        id: Uuid::new_v4(), -        date, -        start: None, -        end: None, -        name: "".to_string(), -        repetition: None, -    } -} -  impl Event {      pub fn pprint(&self) -> String {          let start = self.start.map(pprint_time).unwrap_or_default(); @@ -44,7 +33,7 @@ impl Event {      }  } -/// Repeated events in an included date range +/// Recurring events in an date range (inclusive)  pub fn repetitions_between(      events: &[Event],      start: NaiveDate, diff --git a/src/model/repetition.rs b/src/model/repetition.rs index 2e790d1..872944a 100644 --- a/src/model/repetition.rs +++ b/src/model/repetition.rs @@ -1,8 +1,15 @@  use chrono::{Datelike, Duration, NaiveDate, Weekday};  use serde::{Deserialize, Serialize}; +use std::collections::HashSet;  #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] -pub enum Repetition { +pub struct Repetition { +    pub frequency: Frequency, +    pub removed_occurences: HashSet<usize>, +} + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] +pub enum Frequency {      Daily { period: u32 },      Monthly { day: DayOfMonth },      Yearly, @@ -40,39 +47,51 @@ impl Repetition {      pub fn between(&self, event: NaiveDate, start: NaiveDate, end: NaiveDate) -> Vec<NaiveDate> {          let repeat = |mut date, next: Box<dyn Fn(NaiveDate) -> NaiveDate>| {              let mut repetitions = vec![]; +            let mut iteration: usize = 0;              while date <= end { -                if date >= event && date >= start { -                    repetitions.push(date) +                if date >= event { +                    if date >= start && !self.removed_occurences.contains(&iteration) { +                        repetitions.push(date) +                    } +                    iteration += 1                  } -                date = next(date) +                date = next(date);              }              repetitions          }; -        match self { -            Repetition::Daily { period } => { -                let n = start.signed_duration_since(event).num_days() % (*period as i64); -                let duration = Duration::days(*period as i64); -                repeat(start - Duration::days(n), Box::new(|d| d + duration)) +        match self.frequency { +            Frequency::Daily { period } => { +                let duration = Duration::days(period as i64); +                repeat(event, Box::new(|d| d + duration))              } -            Repetition::Monthly { +            Frequency::Monthly {                  day: DayOfMonth::Day { day }, -            } => match start.with_day(*day as u32) { +            } => match event.with_day(day as u32) {                  Some(first_repetition) => repeat(first_repetition, Box::new(next_month)),                  None => vec![],              }, -            Repetition::Monthly { +            Frequency::Monthly {                  day: DayOfMonth::Weekday { weekday },              } => repeat( -                first_weekday_of_month(start, *weekday), -                Box::new(|d| first_weekday_of_month(next_month(d), *weekday)), +                first_weekday_of_month(event, weekday), +                Box::new(|d| first_weekday_of_month(next_month(d), weekday)),              ), -            Repetition::Yearly => repeat( -                NaiveDate::from_ymd(start.year(), event.month(), event.day()), +            Frequency::Yearly => repeat( +                NaiveDate::from_ymd(event.year(), event.month(), event.day()),                  Box::new(|d| NaiveDate::from_ymd(d.year() + 1, d.month(), d.day())),              ),          }      } + +    pub fn occurence_index(&self, event: NaiveDate, date: NaiveDate) -> Option<usize> { +        let mut without_removed_occurences = self.clone(); +        without_removed_occurences.removed_occurences = HashSet::new(); +        without_removed_occurences +            .between(event, event, date) +            .iter() +            .position(|d| d == &date) +    }  }  fn first_weekday_of_month(date: NaiveDate, weekday: Weekday) -> NaiveDate { @@ -93,7 +112,7 @@ mod tests {      #[test]      fn every_day_event_before() { -        let repetition = Repetition::Daily { period: 1 }; +        let repetition = from_freq(Frequency::Daily { period: 1 });          assert_eq!(              repetition.between(d(2022, 6, 1), d(2022, 7, 1), d(2022, 8, 31)),              d(2022, 7, 1) @@ -105,7 +124,7 @@ mod tests {      #[test]      fn every_day_event_between() { -        let repetition = Repetition::Daily { period: 1 }; +        let repetition = from_freq(Frequency::Daily { period: 1 });          assert_eq!(              repetition.between(d(2022, 8, 10), d(2022, 7, 1), d(2022, 8, 31)),              d(2022, 8, 10) @@ -117,7 +136,7 @@ mod tests {      #[test]      fn every_day_event_after() { -        let repetition = Repetition::Daily { period: 1 }; +        let repetition = from_freq(Frequency::Daily { period: 1 });          assert!(repetition              .between(d(2022, 9, 1), d(2022, 7, 1), d(2022, 8, 31))              .is_empty()) @@ -125,7 +144,7 @@ mod tests {      #[test]      fn every_three_days() { -        let repetition = Repetition::Daily { period: 3 }; +        let repetition = from_freq(Frequency::Daily { period: 3 });          assert_eq!(              repetition.between(d(2022, 2, 16), d(2022, 2, 21), d(2022, 3, 6)),              vec!( @@ -140,9 +159,9 @@ mod tests {      #[test]      fn day_of_month() { -        let repetition = Repetition::Monthly { +        let repetition = from_freq(Frequency::Monthly {              day: DayOfMonth::Day { day: 8 }, -        }; +        });          assert_eq!(              repetition.between(d(2022, 2, 7), d(2022, 1, 1), d(2022, 4, 7)),              vec!(d(2022, 2, 8), d(2022, 3, 8)) @@ -151,11 +170,11 @@ mod tests {      #[test]      fn weekday_of_month() { -        let repetition = Repetition::Monthly { +        let repetition = from_freq(Frequency::Monthly {              day: DayOfMonth::Weekday {                  weekday: Weekday::Tue,              }, -        }; +        });          assert_eq!(              repetition.between(d(2022, 1, 5), d(2022, 1, 1), d(2022, 4, 4)),              vec!(d(2022, 2, 1), d(2022, 3, 1)) @@ -164,14 +183,87 @@ mod tests {      #[test]      fn yearly() { -        let repetition = Repetition::Yearly; +        let repetition = from_freq(Frequency::Yearly);          assert_eq!(              repetition.between(d(2020, 5, 5), d(2018, 1, 1), d(2022, 5, 5)),              vec!(d(2020, 5, 5), d(2021, 5, 5), d(2022, 5, 5))          )      } +    #[test] +    fn every_two_days_removed_occurence() { +        let repetition = Repetition { +            frequency: Frequency::Daily { period: 2 }, +            removed_occurences: HashSet::from([0, 2, 3]), +        }; +        assert_eq!( +            repetition.between(d(2020, 7, 1), d(2020, 7, 1), d(2020, 7, 9)), +            vec!(d(2020, 7, 3), d(2020, 7, 9)) +        ) +    } + +    #[test] +    fn day_of_month_removed_occurence() { +        let repetition = Repetition { +            frequency: Frequency::Monthly { +                day: DayOfMonth::Day { day: 8 }, +            }, +            removed_occurences: HashSet::from([1, 3]), +        }; +        assert_eq!( +            repetition.between(d(2020, 1, 8), d(2020, 1, 8), d(2020, 4, 8)), +            vec!(d(2020, 1, 8), d(2020, 3, 8)) +        ) +    } + +    #[test] +    fn weekday_of_month_removed_occurence() { +        let repetition = Repetition { +            frequency: Frequency::Monthly { +                day: DayOfMonth::Weekday { +                    weekday: Weekday::Fri, +                }, +            }, +            removed_occurences: HashSet::from([1, 2, 3]), +        }; +        assert_eq!( +            repetition.between(d(2020, 2, 1), d(2020, 2, 1), d(2020, 7, 1)), +            vec!(d(2020, 2, 7), d(2020, 6, 5)) +        ) +    } + +    #[test] +    fn yearly_removed_occurence() { +        let repetition = Repetition { +            frequency: Frequency::Yearly, +            removed_occurences: HashSet::from([3]), +        }; +        assert_eq!( +            repetition.between(d(2018, 5, 5), d(2019, 8, 1), d(2022, 5, 5)), +            vec!(d(2020, 5, 5), d(2022, 5, 5)) +        ) +    } + +    #[test] +    fn occurence_index_after_removed_occurence() { +        let repetition = Repetition { +            frequency: Frequency::Yearly, +            removed_occurences: HashSet::from([1]), +        }; +        assert_eq!( +            repetition.occurence_index(d(2020, 1, 1), d(2022, 1, 1)), +            Some(2) +        ) +    } +      fn d(y: i32, m: u32, d: u32) -> NaiveDate {          NaiveDate::from_ymd(y, m, d)      } + +    fn from_freq(frequency: Frequency) -> Repetition { +        Repetition { +            frequency, +            removed_occurences: HashSet::new(), +        } +    }  } | 
