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 assert2::{assert, let_assert};
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 let_assert!(Ok(_) = TaskLineIter::new(&mut e_iter, "2021-06-17", "2021-06-18"));
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 let_assert!(Err(err) = TaskLineIter::new(&mut e_iter, "foo", "2021-06-18"));
200 assert!(err == 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 let_assert!(Err(err) = TaskLineIter::new(&mut e_iter, "2021-06-17", "bar"));
209 assert!(err == 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 let_assert!(Err(err) = TaskLineIter::new(&mut e_iter, "2021-06-17", "2021-06-14"));
218 assert!(err == 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_assert!(Ok(mut iter) = TaskLineIter::new(&mut e_iter, "2021-04-17", "2021-04-18"));
253 assert!(iter.next() == None);
254 }
255
256 #[test]
257 fn test_after_tasks() {
258 let tasks = make_entity_lines();
259
260 let mut e_iter = tasks.into_iter();
261 let_assert!(Ok(mut iter) = TaskLineIter::new(&mut e_iter, "2021-06-17", "2021-06-18"));
262 assert!(iter.next() == None);
263 }
264
265 #[test]
266 fn test_skip_beginning() {
267 let tasks = make_entity_lines();
268
269 let mut e_iter = tasks.into_iter();
270 let_assert!(Ok(mut iter) = TaskLineIter::new(&mut e_iter, "2021-06-01", "2021-06-02"));
271 let_assert!(Some(line) = iter.next());
272 assert!(line == String::from("2021-06-01 08:00:00 tuesday 1"));
273 let_assert!(Some(line) = iter.next());
274 assert!(line == String::from("2021-06-01 08:30:00 tuesday 2"));
275 assert!(iter.next() == None);
276 }
277
278 #[test]
279 fn test_multiple_days() {
280 let tasks = make_entity_lines();
281
282 let mut e_iter = tasks.into_iter();
283 let_assert!(Ok(mut iter) = TaskLineIter::new(&mut e_iter, "2021-06-01", "2021-06-04"));
284 let_assert!(Some(line) = iter.next());
285 assert!(line == String::from("2021-06-01 08:00:00 tuesday 1"));
286 let_assert!(Some(line) = iter.next());
287 assert!(line == String::from("2021-06-01 08:30:00 tuesday 2"));
288 let_assert!(Some(line) = iter.next());
289 assert!(line == String::from("2021-06-02 08:00:00 wednesday 1"));
290 let_assert!(Some(line) = iter.next());
291 assert!(line == String::from("2021-06-02 08:30:00 wednesday 2"));
292 let_assert!(Some(line) = iter.next());
293 assert!(line == String::from("2021-06-03 08:00:00 thursday 1"));
294 let_assert!(Some(line) = iter.next());
295 assert!(line == String::from("2021-06-03 08:30:00 thursday 2"));
296 assert!(iter.next() == None);
297 }
298
299 #[test]
300 fn test_start_in_gap() {
301 let tasks = make_entity_lines();
302
303 let mut e_iter = tasks.into_iter();
304 let_assert!(Ok(mut iter) = TaskLineIter::new(&mut e_iter, "2021-06-05", "2021-06-08"));
305 let_assert!(Some(line) = iter.next());
306 assert!(line == String::from("2021-06-07 08:00:00 monday 1"));
307 let_assert!(Some(line) = iter.next());
308 assert!(line == String::from("2021-06-07 08:30:00 monday 2"));
309 assert!(iter.next() == None);
310 }
311
312 #[test]
313 fn test_end_in_gap() {
314 let tasks = make_entity_lines();
315
316 let mut e_iter = tasks.into_iter();
317 let_assert!(Ok(mut iter) = TaskLineIter::new(&mut e_iter, "2021-06-04", "2021-06-06"));
318 let_assert!(Some(line) = iter.next());
319 assert!(line == String::from("2021-06-04 08:00:00 friday 1"));
320 let_assert!(Some(line) = iter.next());
321 assert!(line == String::from("2021-06-04 08:30:00 friday 2"));
322 assert!(iter.next() == None);
323 }
324
325 #[test]
326 fn test_missing_in_range() {
327 let tasks = make_entity_lines();
328
329 let mut e_iter = tasks.into_iter();
330 let_assert!(Ok(mut iter) = TaskLineIter::new(&mut e_iter, "2021-06-06", "2021-06-07"));
331 assert!(iter.next() == None);
332 }
333
334 #[test]
335 fn test_to_end() {
336 let tasks = make_entity_lines();
337
338 let mut e_iter = tasks.into_iter();
339 let_assert!(Ok(mut iter) = TaskLineIter::new(&mut e_iter, "2021-06-07", "2021-06-10"));
340 let_assert!(Some(line) = iter.next());
341 assert!(line == String::from("2021-06-07 08:00:00 monday 1"));
342 let_assert!(Some(line) = iter.next());
343 assert!(line == String::from("2021-06-07 08:30:00 monday 2"));
344 let_assert!(Some(line) = iter.next());
345 assert!(line == String::from("2021-06-08 08:00:00 final"));
346 assert!(iter.next() == None);
347 }
348}