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 { return None; }
33 let h = mins / 60;
34 let m = mins % 60;
35 if m > 0 {
36 Some(format!("{}h{}m", h, m))
37 } else {
38 Some(format!("{}h", h))
39 }
40 }
41}
42
43fn parse_hhmm(s: &str) -> Option<(i64, i64)> {
44 let mut parts = s.splitn(2, ':');
45 let h: i64 = parts.next()?.trim().parse().ok()?;
46 let m: i64 = parts.next()?.trim().parse().ok()?;
47 Some((h, m))
48}
49
50#[cfg(test)]
51mod tests {
52 use super::*;
53
54 #[test]
55 fn test_duration_str() {
56 let d = LogData::parse_args("work, start: 09:00, end: 12:30");
57 assert_eq!(d.duration_str(), Some("3h30m".into()));
58 }
59
60 #[test]
61 fn test_exact_hours() {
62 let d = LogData::parse_args("start: 09:00, end: 12:00");
63 assert_eq!(d.duration_str(), Some("3h".into()));
64 }
65
66 #[test]
67 fn test_no_times() {
68 let d = LogData::parse_args("work");
69 assert_eq!(d.duration_str(), None);
70 }
71
72 #[test]
73 fn test_duration_minutes() {
74 let d = LogData::parse_args("start: 09:00, end: 12:30");
75 assert_eq!(d.duration_minutes(), Some(210));
76 }
77}