1use std::time::Duration;
9
10use crate::TaskEvent;
11
12fn hour() -> Duration { Duration::from_secs(3600) }
13
14pub struct DayHours {
16 start: usize,
18 hours: Vec<Hour>
20}
21
22impl DayHours {
23 pub fn start(&self) -> usize { self.start }
25
26 pub fn end(&self) -> usize { self.start() + self.hours.len() - 1 }
28
29 pub fn num_hours(&self) -> usize { self.hours.len() }
31
32 pub fn add(&mut self, event: TaskEvent) {
34 let hour = event.hour();
35 if self.hours.is_empty() {
36 self.start = hour;
37 self.hours.push(Hour::default());
38 }
39 if hour > self.end() {
40 (0..(hour - self.end())).for_each(|_| self.hours.push(Hour::default()));
41 }
42 let start = self.start();
43 if let Some(the_hour) = self.hours.get_mut(hour - start) {
44 if let Some(ev) = the_hour.add(event) {
45 self.add(ev);
46 }
47 }
48 }
49
50 pub fn iter(&self) -> impl Iterator<Item = &'_ Hour> { self.hours.iter() }
52}
53
54impl Default for DayHours {
55 fn default() -> Self { Self { start: 0, hours: Vec::new() } }
57}
58
59#[derive(Debug, Clone, Eq, PartialEq, PartialOrd)]
61pub struct Hour {
62 events: Vec<TaskEvent>,
63 offset: Duration
64}
65
66impl Hour {
67 pub fn is_empty(&self) -> bool { self.events.is_empty() }
69
70 pub fn iter(&self) -> impl Iterator<Item = &'_ TaskEvent> { self.events.iter() }
72
73 pub fn remain(&self) -> Duration { hour() - self.offset }
76
77 pub fn add(&mut self, event: TaskEvent) -> Option<TaskEvent> {
80 self.offset = Duration::from_secs(u64::from(event.second_offset()));
81 if event.duration() >= self.remain() {
82 let (first, next) = event.split(self.remain())?;
83 self.events.push(first);
84 self.offset = hour();
85 Some(next)
86 }
87 else {
88 self.offset += event.duration();
89 self.events.push(event);
90 None
91 }
92 }
93}
94
95impl Default for Hour {
96 fn default() -> Self { Self { events: vec![], offset: Duration::default() } }
98}
99
100#[cfg(test)]
101mod tests {
102 use spectral::prelude::*;
103
104 use super::*;
105 use crate::date::DateTime;
106
107 fn task_event(time: (u32, u32, u32), proj: &str, secs: u64) -> TaskEvent {
108 TaskEvent::new(
109 DateTime::new((2022, 2, 17), time).unwrap(),
110 proj,
111 Duration::from_secs(secs)
112 )
113 }
114
115 #[test]
116 fn test_hour_new() {
117 let hour = Hour::default();
118 assert_that!(hour.is_empty()).is_equal_to(true);
119 assert_that!(hour.iter().next()).is_none();
120 assert_that!(hour.remain()).is_equal_to(Duration::from_secs(3600));
121 }
122
123 #[test]
124 fn test_hour_add_partial() {
125 let mut hour = Hour::default();
126 let dur = task_event((8, 0, 0), "foo", 300);
127
128 assert_that!(hour.add(dur.clone())).is_none();
129
130 assert_that!(hour.is_empty()).is_equal_to(false);
131 assert_that!(hour.iter().next()).contains(&dur);
132 }
133
134 #[test]
135 fn test_hour_add_partial_remain() {
136 let mut hour = Hour::default();
137 let dur = task_event((8, 0, 0), "foo", 300);
138
139 assert_that!(hour.add(dur.clone())).is_none();
140
141 assert_that!(hour.remain()).is_equal_to(Duration::from_secs(3300));
142 let used: Duration = hour.iter().map(|d| d.duration()).sum();
143 assert_that!(hour.remain()).is_equal_to(Duration::from_secs(3600) - used);
144 }
145
146 #[test]
147 fn test_hour_add_partial_again() {
148 let mut hour = Hour::default();
149 let durs = [
150 task_event((8, 0, 0), "foo", 300),
151 task_event((8, 5, 0), "bar", 600)
152 ];
153
154 for dur in durs.iter() {
155 assert_that!(hour.add(dur.clone())).is_none();
156 }
157
158 assert_that!(hour.is_empty()).is_equal_to(false);
159
160 for (dur, hdur) in durs.iter().zip(hour.iter()) {
161 assert_that!(hdur).is_equal_to(&dur);
162 }
163 }
164
165 #[test]
166 fn test_hour_add_partial_again_remain() {
167 let mut hour = Hour::default();
168 let durs = [
169 task_event((8, 0, 0), "foo", 300),
170 task_event((8, 5, 0), "bar", 600)
171 ];
172
173 for dur in durs.iter() {
174 assert_that!(hour.add(dur.clone())).is_none();
175 }
176
177 assert_that!(hour.remain()).is_equal_to(Duration::from_secs(2700));
178 let used: Duration = hour.iter().map(|d| d.duration()).sum();
179 assert_that!(hour.remain()).is_equal_to(Duration::from_secs(3600) - used);
180 }
181
182 #[test]
183 fn test_hour_add_overflow() {
184 let mut hour = Hour::default();
185 let dur1 = task_event((8, 0, 0), "foo", 3300);
186 let dur2 = task_event((8, 55, 0), "bar", 650);
187
188 assert_that!(hour.add(dur1.clone())).is_none();
189 assert_that!(hour.add(dur2.clone())).is_some();
190
191 let expect = [dur1, task_event((8, 55, 0), "bar", 300)];
192 for (dur, hdur) in expect.iter().zip(hour.iter()) {
193 assert_that!(hdur).is_equal_to(&dur);
194 }
195 }
196
197 #[test]
198 fn test_hour_add_overflow_remain() {
199 let mut hour = Hour::default();
200 let dur1 = task_event((8, 0, 0), "foo", 3300);
201 let dur2 = task_event((8, 55, 0), "bar", 650);
202 let remain = task_event((9, 0, 0), "bar", 350);
203
204 assert_that!(hour.add(dur1.clone())).is_none();
205 assert_that!(hour.add(dur2.clone())).contains(remain);
206
207 assert_that!(hour.remain()).is_equal_to(Duration::from_secs(0));
208 let used: Duration = hour.iter().map(|d| d.duration()).sum();
209 assert_that!(used).is_equal_to(Duration::from_secs(3600));
210 }
211}