1use super::split_args;
2
3#[derive(Debug, Clone)]
4pub struct LogData {
5 pub tags: Vec<String>,
6 pub start: Option<String>, pub end: Option<String>, }
9
10impl LogData {
11 pub fn parse_args(raw: &str) -> Self {
12 let p = split_args(raw);
13 LogData {
14 tags: p.tags,
15 start: p.attrs.get("start").cloned(),
16 end: p.attrs.get("end").cloned(),
17 }
18 }
19
20 pub fn duration_minutes(&self) -> Option<i64> {
22 let s = self.start.as_deref()?;
23 let e = self.end.as_deref()?;
24 let (sh, sm) = parse_hhmm(s)?;
25 let (eh, em) = parse_hhmm(e)?;
26 Some((eh * 60 + em) - (sh * 60 + sm))
27 }
28
29 pub fn duration_str(&self) -> Option<String> {
31 let mins = self.duration_minutes()?;
32 if mins <= 0 {
33 return None;
34 }
35 let h = mins / 60;
36 let m = mins % 60;
37 if m > 0 {
38 Some(format!("{}h{}m", h, m))
39 } else {
40 Some(format!("{}h", h))
41 }
42 }
43}
44
45fn parse_hhmm(s: &str) -> Option<(i64, i64)> {
46 let mut parts = s.splitn(2, ':');
47 let h: i64 = parts.next()?.trim().parse().ok()?;
48 let m: i64 = parts.next()?.trim().parse().ok()?;
49 Some((h, m))
50}
51
52#[cfg(test)]
53mod tests {
54 use super::*;
55
56 #[test]
57 fn test_duration_str() {
58 let d = LogData::parse_args("work, start: 09:00, end: 12:30");
59 assert_eq!(d.duration_str(), Some("3h30m".into()));
60 }
61
62 #[test]
63 fn test_exact_hours() {
64 let d = LogData::parse_args("start: 09:00, end: 12:00");
65 assert_eq!(d.duration_str(), Some("3h".into()));
66 }
67
68 #[test]
69 fn test_no_times() {
70 let d = LogData::parse_args("work");
71 assert_eq!(d.duration_str(), None);
72 }
73
74 #[test]
75 fn test_duration_minutes() {
76 let d = LogData::parse_args("start: 09:00, end: 12:30");
77 assert_eq!(d.duration_minutes(), Some(210));
78 }
79}