calib/
lib.rs

1use thiserror::Error;
2
3mod cal;
4mod event;
5
6pub use cal::EventCalendar;
7pub use event::Event;
8
9/// Basic Errors that can occur for events
10#[derive(Error, Debug)]
11pub enum EventError {
12    /// Error for invalid start time for an event
13    #[error("start time/date cannot be after end time/date")]
14    InvalidStartTime,
15
16    /// Error for invalid end time for an event
17    #[error("end time/date cannot be before start time/date")]
18    InvalidEndTime,
19}
20
21// NOTE: How to represent events that last multiple days?
22// NOTE: In the future it migh be worth trying to remove the Day struct, it feels redundant
23//       Maybe a Vector or Hashmap of Events makes sense? Suppose a request
24//       was made to get all of the events given some time range,
25
26#[cfg(test)]
27mod test {
28    use chrono::{Datelike, NaiveDate, NaiveDateTime, NaiveTime, Timelike};
29
30    use super::*;
31
32    // helper functions for test
33    /// return the first NaiveDate for 2023
34    fn first_day_2023_nd() -> NaiveDate {
35        NaiveDate::from_ymd_opt(2023, 1, 1).unwrap()
36    }
37
38    /// return the first time of any day 00:00:00
39    fn first_time_nt() -> NaiveTime {
40        NaiveTime::from_hms_opt(0, 0, 0).unwrap()
41    }
42
43    /// return the last time for any day 23:59:59
44    fn last_time_nt() -> NaiveTime {
45        NaiveTime::from_hms_opt(23, 59, 59).unwrap()
46    }
47
48    /// return the first NaiveDateTime for 2023 - 01/01/2023-00:00:00
49    fn first_day_2023_ndt() -> NaiveDateTime {
50        let nd = first_day_2023_nd();
51        let nt = first_time_nt();
52        NaiveDateTime::new(nd, nt)
53    }
54
55    // ##################################
56    // ###           TESTS            ###
57    // ##################################
58
59    #[test]
60    fn test_new_event() {
61        let naive_date = first_day_2023_nd();
62
63        // common times
64        let first_time = first_time_nt();
65        let last_time = last_time_nt();
66
67        // event being tested
68        let event = Event::new(String::from("Birthday Party"), &naive_date);
69
70        // assumed start and end times for testing
71        let assumed_start_time = NaiveDateTime::new(naive_date, first_time);
72        let assumed_end_time = NaiveDateTime::new(naive_date, last_time);
73
74        assert_eq!(event.start(), assumed_start_time);
75        assert_eq!(event.end(), assumed_end_time);
76    }
77
78    #[test]
79    fn test_event_start_time_change() {
80        // basic date declaration
81        let naive_date = first_day_2023_nd();
82
83        // event being tested
84        let mut event = Event::new(String::from("Birthday Party"), &naive_date);
85        // new start time
86        let new_start_time = NaiveTime::from_hms_opt(10, 30, 0).unwrap();
87
88        event = event
89            .with_start(NaiveDateTime::new(naive_date, new_start_time))
90            .unwrap();
91        assert_eq!(
92            event.start(),
93            NaiveDateTime::new(naive_date, new_start_time)
94        )
95    }
96
97    #[test]
98    fn test_event_end_time_change() {
99        // basic date declaration
100        let naive_date = first_day_2023_nd();
101
102        // event being tested
103        let mut event = Event::new(String::from("Birthday Party"), &naive_date);
104        // new start time
105        let new_end_time = NaiveTime::from_hms_opt(22, 30, 0).unwrap();
106
107        event = event
108            .with_end(NaiveDateTime::new(naive_date, new_end_time))
109            .unwrap();
110
111        assert_eq!(event.end(), NaiveDateTime::new(naive_date, new_end_time))
112    }
113
114    #[test]
115    fn test_invalid_event_time_change() {
116        // basic date declaration
117        let naive_date = first_day_2023_nd();
118        let start_time = NaiveTime::from_hms_opt(12, 0, 0).unwrap();
119        let invalid_end_time = NaiveTime::from_hms_opt(10, 0, 0).unwrap();
120
121        let mut event = Event::new("Birthday".into(), &naive_date);
122
123        event = event
124            .with_start(NaiveDateTime::new(naive_date, start_time))
125            .unwrap();
126
127        assert_eq!(
128            true,
129            event
130                .with_end(NaiveDateTime::new(naive_date, invalid_end_time))
131                .is_err()
132        );
133    }
134
135    #[test]
136    fn invalid_events_test() {
137        // basic date declaration
138        let naive_date = first_day_2023_nd();
139
140        // common times
141        let first_time = first_time_nt();
142        let last_time = last_time_nt();
143
144        // event being tested
145        let mut event = Event::new(String::from("Birthday Party"), &naive_date);
146
147        // assumed start and end times for testing
148        let assumed_start_time = NaiveDateTime::new(naive_date, first_time);
149        let assumed_end_time = NaiveDateTime::new(naive_date, last_time);
150
151        assert_eq!(event.start(), assumed_start_time);
152        assert_eq!(event.end(), assumed_end_time);
153
154        // new start time
155        let new_start_time = NaiveTime::from_hms_opt(10, 30, 0).unwrap();
156
157        // update start time
158        event = event
159            .with_start(NaiveDateTime::new(naive_date, new_start_time))
160            .unwrap();
161
162        assert_eq!(
163            event.start(),
164            NaiveDateTime::new(naive_date, new_start_time)
165        );
166
167        // new end time
168        let new_end_time = NaiveTime::from_hms_opt(22, 30, 0).unwrap();
169
170        // update end time
171        event = event
172            .with_end(NaiveDateTime::new(naive_date, new_end_time))
173            .unwrap();
174
175        assert_eq!(event.end(), NaiveDateTime::new(naive_date, new_end_time));
176
177        // try to set invalid start time
178        let status = event.with_start(NaiveDateTime::new(naive_date, last_time));
179        assert_eq!(true, status.is_err());
180
181        // try to set invalid end time
182        let event = Event::new(String::from("Birthday Party"), &naive_date);
183        let status = event.with_end(NaiveDateTime::new(naive_date, first_time));
184        assert_eq!(true, status.is_err());
185    }
186
187    #[test]
188    fn test_event_ordering_lt_start_cmp() {
189        use std::cmp::Ordering;
190        let ndt = first_day_2023_ndt();
191        let d1 = Event::new("A".into(), &ndt.date());
192
193        // 01/01/2023-00:00:00 < 01/01/2023-00:00:01
194        let mut d2 = Event::new("A".into(), &ndt.date());
195        d2 = d2.with_start(d1.start().with_second(1).unwrap()).unwrap();
196        assert_eq!(d1.cmp(&d2), Ordering::Less);
197
198        // 01/01/2023-00:00:00 < 01/01/2023-00:01:00
199        let mut d3 = Event::new("A".into(), &ndt.date());
200        d3 = d3.with_start(d1.start().with_minute(1).unwrap()).unwrap();
201        assert_eq!(d1.cmp(&d3), Ordering::Less);
202
203        // 01/01/2023-00:00:00 < 01/01/2023-01:00:00
204        let mut d4 = Event::new("A".into(), &ndt.date());
205        d4 = d4.with_start(d1.start().with_hour(1).unwrap()).unwrap();
206        assert_eq!(d1.cmp(&d4), Ordering::Less);
207
208        // 01/01/2023-00:00:00 < 01/01/2024-00:00:00
209        let d5 = Event::new("A".into(), &ndt.date().with_year(2024).unwrap());
210        assert_eq!(d1.cmp(&d5), Ordering::Less);
211
212        // 01/01/2023-00:00:00 < 01/02/2023-00:00:00
213        let mut d6 = Event::new("A".into(), &ndt.date());
214        d6 = d6.with_end(d1.start().with_day(3).unwrap()).unwrap();
215        d6 = d6.with_start(d1.start().with_day(2).unwrap()).unwrap();
216        assert_eq!(d1.cmp(&d6), Ordering::Less);
217
218        // 01/01/2023-00:00:00 < 02/01/2023-00:00:00
219        let mut d7 = Event::new("A".into(), &ndt.date());
220        d7 = d7.with_end(d1.start().with_month(3).unwrap()).unwrap();
221        d7 = d7.with_start(d1.start().with_month(2).unwrap()).unwrap();
222        assert_eq!(d1.cmp(&d7), Ordering::Less);
223    }
224
225    #[test]
226    fn test_event_range() {
227        let nd1 = first_day_2023_nd();
228        let nd2 = nd1.with_day(2).unwrap();
229        let nd3 = nd1.with_day(3).unwrap();
230        let nd4 = nd1.with_day(4).unwrap();
231        let nd5 = nd1.with_day(5).unwrap();
232
233        let e1 = Event::new("A".into(), &nd1);
234        let e2 = Event::new("A".into(), &nd2);
235        let e3 = Event::new("A".into(), &nd3);
236        let e4 = Event::new("A".into(), &nd4);
237        let e5 = Event::new("A".into(), &nd5);
238
239        let range_start = NaiveDateTime::new(nd2, NaiveTime::from_hms_opt(11, 0, 0).unwrap());
240        let range_end = NaiveDateTime::new(nd4, last_time_nt());
241
242        let mut cal = EventCalendar::default();
243        cal.add_event(e1);
244        cal.add_event(e2);
245        cal.add_event(e3);
246        cal.add_event(e4);
247        cal.add_event(e5);
248
249        let mut iter = cal.events_in_range(range_start, range_end);
250        assert_eq!(
251            iter.next().map(|(_, e)| e),
252            Some(&Event::new("A".into(), &nd2))
253        );
254        assert_eq!(
255            iter.next().map(|(_, e)| e),
256            Some(&Event::new("A".into(), &nd3))
257        );
258        assert_eq!(
259            iter.next().map(|(_, e)| e),
260            Some(&Event::new("A".into(), &nd4))
261        );
262        assert_eq!(iter.next(), None);
263    }
264
265    #[test]
266    fn test_event_serialize() {
267        let nd = first_day_2023_nd();
268        let e = Event::new("A".into(), &nd);
269
270        let first_time = first_day_2023_ndt().format("%Y-%m-%dT%H:%M:%S").to_string();
271        let last_time = NaiveDateTime::new(nd, last_time_nt())
272            .format("%Y-%m-%dT%H:%M:%S")
273            .to_string();
274
275        assert_eq!(
276            e.serialize(),
277            format!("{{\"start\":\"{first_time}\",\"end\":\"{last_time}\",\"name\":\"A\"}}",)
278        )
279    }
280}