timelog/
task_line_iter.rs1use std::fmt::{self, Debug};
27use std::result;
28
29use once_cell::sync::Lazy;
30use regex::Regex;
31
32#[doc(inline)]
33use crate::date::DateError;
34use crate::error::Error;
35use crate::Entry;
36
37#[derive(Debug)]
39enum Stage {
40 Before,
42 In,
44 After
46}
47
48pub struct TaskLineIter<'a, I>
50where
51 I: Iterator<Item = String>
52{
53 iter: I,
55 stage: Stage,
57 start: &'a str,
59 end: &'a str
61}
62
63impl<'a, I> Debug for TaskLineIter<'a, I>
64where
65 I: Iterator<Item = String>
66{
67 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> result::Result<(), fmt::Error> {
69 f.debug_struct("TaskLineIter")
70 .field("iter", &"Iter")
71 .field("stage", &self.stage)
72 .field("start", &self.start)
73 .field("end", &self.end)
74 .finish()
75 }
76}
77
78static DATE: Lazy<Regex> =
83 Lazy::new(|| Regex::new(r"^\d{4}-\d\d-\d\d$").expect("Date regex invalid"));
84
85impl<'a, I> TaskLineIter<'a, I>
86where
87 I: Iterator<Item = String>
88{
89 pub fn new(iter: I, start: &'a str, end: &'a str) -> crate::Result<Self> {
98 if !DATE.is_match(start) {
99 return Err(Error::StartDateFormat);
100 }
101 if !DATE.is_match(end) {
102 return Err(Error::EndDateFormat);
103 }
104 if start >= end {
105 return Err(DateError::WrongDateOrder.into());
106 }
107
108 Ok(Self { iter, stage: Stage::Before, start, end })
109 }
110
111 pub fn last_line_before(mut self) -> Option<String> {
114 let mut prev: Option<String> = None;
115 for line in self.iter.by_ref() {
116 if let Some(date) = Entry::date_from_line(&line) {
117 if self.start <= date && date < self.end {
118 return prev;
119 }
120 prev = Some(line);
121 }
122 }
123 None
124 }
125
126 fn find_first(&mut self) -> Option<String> {
128 for line in self.iter.by_ref() {
129 if let Some(date) = Entry::date_from_line(&line) {
130 if self.start <= date && date < self.end {
131 return Some(line);
132 }
133 }
134 }
135 None
136 }
137
138 fn get_next(&mut self) -> Option<String> {
141 if let Some(line) = self.iter.next() {
142 if let Some(date) = Entry::date_from_line(&line) {
143 if date < self.end {
144 return Some(line);
145 }
146 }
147 }
148 None
149 }
150}
151
152impl<'a, I> Iterator for TaskLineIter<'a, I>
153where
154 I: Iterator<Item = String>
155{
156 type Item = String;
157
158 fn next(&mut self) -> Option<Self::Item> {
160 match self.stage {
161 Stage::Before => {
162 if let Some(line) = self.find_first() {
163 self.stage = Stage::In;
164 return Some(line);
165 }
166 }
167 Stage::In => {
168 if let Some(line) = self.get_next() {
169 return Some(line);
170 }
171 }
172 Stage::After => {}
173 }
174 self.stage = Stage::After;
175 None
176 }
177}
178
179#[cfg(test)]
180mod tests {
181 use spectral::prelude::*;
182
183 use super::*;
184 use crate::date::DateError;
185
186 #[test]
187 fn test_successful_new() {
188 let tasks: Vec<String> = Vec::new();
189
190 let mut e_iter = tasks.into_iter();
191 assert_that!(TaskLineIter::new(&mut e_iter, "2021-06-17", "2021-06-18")).is_ok();
192 }
193
194 #[test]
195 fn test_new_bad_start() {
196 let tasks: Vec<String> = Vec::new();
197
198 let mut e_iter = tasks.into_iter();
199 assert_that!(TaskLineIter::new(&mut e_iter, "foo", "2021-06-18"))
200 .is_err_containing(Error::StartDateFormat);
201 }
202
203 #[test]
204 fn test_new_bad_end() {
205 let tasks: Vec<String> = Vec::new();
206
207 let mut e_iter = tasks.into_iter();
208 assert_that!(TaskLineIter::new(&mut e_iter, "2021-06-17", "bar"))
209 .is_err_containing(Error::EndDateFormat);
210 }
211
212 #[test]
213 fn test_new_wrong_order() {
214 let tasks: Vec<String> = Vec::new();
215
216 let mut e_iter = tasks.into_iter();
217 assert_that!(TaskLineIter::new(&mut e_iter, "2021-06-17", "2021-06-14"))
218 .is_err_containing(Error::DateError { source: DateError::WrongDateOrder });
219 }
220
221 fn make_entity_lines() -> Vec<String> {
222 [
223 "2021-05-30 08:00:00 junk",
224 "2021-05-30 08:10:00 junk",
225 "2021-05-30 08:20:00 junk",
226 "2021-05-30 08:30:00 junk",
227 "2021-05-30 08:40:00 junk",
228 "2021-05-30 08:50:00 junk",
229 "2021-05-30 09:00:00 junk",
230 "2021-06-01 08:00:00 tuesday 1",
231 "2021-06-01 08:30:00 tuesday 2",
232 "2021-06-02 08:00:00 wednesday 1",
233 "2021-06-02 08:30:00 wednesday 2",
234 "2021-06-03 08:00:00 thursday 1",
235 "2021-06-03 08:30:00 thursday 2",
236 "2021-06-04 08:00:00 friday 1",
237 "2021-06-04 08:30:00 friday 2",
238 "2021-06-07 08:00:00 monday 1",
239 "2021-06-07 08:30:00 monday 2",
240 "2021-06-08 08:00:00 final"
241 ]
242 .iter()
243 .map(|&s| String::from(s))
244 .collect()
245 }
246
247 #[test]
248 fn test_before_tasks() {
249 let tasks = make_entity_lines();
250
251 let mut e_iter = tasks.into_iter();
252 let mut iter = TaskLineIter::new(&mut e_iter, "2021-04-17", "2021-04-18")
253 .expect("Bad iterator construction");
254 assert_that!(iter.next()).is_none();
255 }
256
257 #[test]
258 fn test_after_tasks() {
259 let tasks = make_entity_lines();
260
261 let mut e_iter = tasks.into_iter();
262 let mut iter = TaskLineIter::new(&mut e_iter, "2021-06-17", "2021-06-18")
263 .expect("Bad iterator construction");
264 assert_that!(iter.next()).is_none();
265 }
266
267 #[test]
268 fn test_skip_beginning() {
269 let tasks = make_entity_lines();
270
271 let mut e_iter = tasks.into_iter();
272 let mut iter = TaskLineIter::new(&mut e_iter, "2021-06-01", "2021-06-02")
273 .expect("Bad iterator construction");
274 assert_that!(iter.next()).contains_value(String::from("2021-06-01 08:00:00 tuesday 1"));
275 assert_that!(iter.next()).contains_value(String::from("2021-06-01 08:30:00 tuesday 2"));
276 assert_that!(iter.next()).is_none();
277 }
278
279 #[test]
280 fn test_multiple_days() {
281 let tasks = make_entity_lines();
282
283 let mut e_iter = tasks.into_iter();
284 let mut iter = TaskLineIter::new(&mut e_iter, "2021-06-01", "2021-06-04")
285 .expect("Bad iterator construction");
286 assert_that!(iter.next()).contains_value(String::from("2021-06-01 08:00:00 tuesday 1"));
287 assert_that!(iter.next()).contains_value(String::from("2021-06-01 08:30:00 tuesday 2"));
288 assert_that!(iter.next()).contains_value(String::from("2021-06-02 08:00:00 wednesday 1"));
289 assert_that!(iter.next()).contains_value(String::from("2021-06-02 08:30:00 wednesday 2"));
290 assert_that!(iter.next()).contains_value(String::from("2021-06-03 08:00:00 thursday 1"));
291 assert_that!(iter.next()).contains_value(String::from("2021-06-03 08:30:00 thursday 2"));
292 assert_that!(iter.next()).is_none();
293 }
294
295 #[test]
296 fn test_start_in_gap() {
297 let tasks = make_entity_lines();
298
299 let mut e_iter = tasks.into_iter();
300 let mut iter = TaskLineIter::new(&mut e_iter, "2021-06-05", "2021-06-08")
301 .expect("Bad iterator construction");
302 assert_that!(iter.next()).contains_value(String::from("2021-06-07 08:00:00 monday 1"));
303 assert_that!(iter.next()).contains_value(String::from("2021-06-07 08:30:00 monday 2"));
304 assert_that!(iter.next()).is_none();
305 }
306
307 #[test]
308 fn test_end_in_gap() {
309 let tasks = make_entity_lines();
310
311 let mut e_iter = tasks.into_iter();
312 let mut iter = TaskLineIter::new(&mut e_iter, "2021-06-04", "2021-06-06")
313 .expect("Bad iterator construction");
314 assert_that!(iter.next()).contains_value(String::from("2021-06-04 08:00:00 friday 1"));
315 assert_that!(iter.next()).contains_value(String::from("2021-06-04 08:30:00 friday 2"));
316 assert_that!(iter.next()).is_none();
317 }
318
319 #[test]
320 fn test_missing_in_range() {
321 let tasks = make_entity_lines();
322
323 let mut e_iter = tasks.into_iter();
324 let mut iter = TaskLineIter::new(&mut e_iter, "2021-06-06", "2021-06-07")
325 .expect("Bad iterator construction");
326 assert_that!(iter.next()).is_none();
327 }
328
329 #[test]
330 fn test_to_end() {
331 let tasks = make_entity_lines();
332
333 let mut e_iter = tasks.into_iter();
334 let mut iter = TaskLineIter::new(&mut e_iter, "2021-06-07", "2021-06-10")
335 .expect("Bad iterator construction");
336 assert_that!(iter.next()).contains_value(String::from("2021-06-07 08:00:00 monday 1"));
337 assert_that!(iter.next()).contains_value(String::from("2021-06-07 08:30:00 monday 2"));
338 assert_that!(iter.next()).contains_value(String::from("2021-06-08 08:00:00 final"));
339 assert_that!(iter.next()).is_none();
340 }
341}