summaryrefslogtreecommitdiff
path: root/src/rruleset_iter.rs
blob: 7b83f39d9dc106c50e334faae2d8b7dca5a010b4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
use crate::rruleset::RRuleSet;
use crate::{rrule_iter::RRuleIter, RRule};
use chrono::prelude::*;
use chrono_tz::Tz;
use std::collections::HashMap;
use std::iter::Iterator;

pub struct RRuleSetIter {
    pub queue: HashMap<usize, DateTime<Tz>>,
    pub rrule_iters: Vec<RRuleIter>,
    pub exrules: Vec<RRule>,
    pub exdates: HashMap<i64, ()>,
    // Sorted additional dates in decreasing order
    pub rdates: Vec<DateTime<Tz>>,
}

impl Iterator for RRuleSetIter {
    type Item = DateTime<Tz>;

    fn next(&mut self) -> Option<Self::Item> {
        let mut next_date: Option<(usize, DateTime<Tz>)> = None;

        for (i, rrule_iter) in self.rrule_iters.iter_mut().enumerate() {
            let rrule_queue = self.queue.remove(&i);
            let next_rrule_date;
            match rrule_queue {
                Some(d) => next_rrule_date = Some(d),
                None => {
                    // should be method on self
                    next_rrule_date = generate(rrule_iter, &mut self.exrules, &mut self.exdates);
                }
            }

            match next_rrule_date {
                Some(next_rrule_date) => match next_date {
                    None => next_date = Some((i, next_rrule_date)),
                    Some(date) => {
                        if date.1 >= next_rrule_date {
                            // Add previous date to its rrule queue
                            self.queue.insert(date.0, date.1);

                            // Update next_date
                            next_date = Some((i, next_rrule_date));
                        } else {
                            // Store for next iterations
                            self.queue.insert(i, next_rrule_date);
                        }
                    }
                },
                None => {}
            }
        }

        // TODO: RDates should be prefiltered before starting iteration
        match generate_date(&mut self.rdates, &mut self.exrules, &mut self.exdates) {
            Some(first_rdate) => {
                let next_date = match next_date {
                    Some(next_date) => {
                        if next_date.1 >= first_rdate {
                            // Add previous date to its rrule queue
                            self.queue.insert(next_date.0, next_date.1);

                            first_rdate
                        } else {
                            // add rdate back
                            self.rdates.push(first_rdate);

                            next_date.1
                        }
                    }
                    None => first_rdate,
                };
                Some(next_date)
            }
            None => next_date.map(|d| d.1),
        }
    }
}

fn generate_date(
    dates: &mut Vec<DateTime<Tz>>,
    exrules: &mut Vec<RRule>,
    exdates: &mut HashMap<i64, ()>,
) -> Option<DateTime<Tz>> {
    if dates.is_empty() {
        return None;
    }

    let mut date = dates.remove(dates.len() - 1);
    while !accept_generated_date(&Some(date), exrules, exdates) {
        if dates.is_empty() {
            return None;
        }
        date = dates.remove(dates.len() - 1);
    }

    Some(date)
}

fn generate(
    rrule_iter: &mut RRuleIter,
    exrules: &mut Vec<RRule>,
    exdates: &mut HashMap<i64, ()>,
) -> Option<DateTime<Tz>> {
    let mut date = rrule_iter.next();
    while !accept_generated_date(&date, exrules, exdates) {
        date = rrule_iter.next();
    }

    date
}

fn accept_generated_date(
    date: &Option<DateTime<Tz>>,
    exrules: &mut Vec<RRule>,
    exdates: &mut HashMap<i64, ()>,
) -> bool {
    match date {
        None => true,
        Some(date) => {
            let dt = date.timestamp();

            if !exrules.is_empty() {
                let after = date.timezone().timestamp(dt - 1, 0);
                let before = date.timezone().timestamp(dt + 1, 0);
                for exrule in exrules.iter_mut() {
                    for date in exrule.between(after, before, true) {
                        exdates.insert(date.timestamp(), ());
                    }
                }
            }

            if exdates.contains_key(&dt) {
                return false;
            }

            true
        }
    }
}

impl IntoIterator for RRuleSet {
    type Item = DateTime<Tz>;

    type IntoIter = RRuleSetIter;

    fn into_iter(mut self) -> Self::IntoIter {
        // Sort in decreasing order
        self.rdate.sort_by(|d1, d2| d2.partial_cmp(d1).unwrap());
        RRuleSetIter {
            queue: Default::default(),
            rrule_iters: self
                .rrule
                .into_iter()
                .map(|rrule| rrule.into_iter())
                .collect(),
            rdates: self.rdate,
            exrules: self.exrule,
            exdates: self
                .exdate
                .iter()
                .map(|exdate| (exdate.timestamp(), ()))
                .collect(),
        }
    }
}