mcpr_integrations/store/
duration.rs1use std::time::Duration;
10
11pub fn parse_duration(s: &str) -> Option<Duration> {
34 let s = s.trim();
35 if s.is_empty() {
36 return None;
37 }
38
39 let (num_str, multiplier) = if let Some(n) = s.strip_suffix('w') {
41 (n, 7 * 24 * 3600)
42 } else if let Some(n) = s.strip_suffix('d') {
43 (n, 24 * 3600)
44 } else if let Some(n) = s.strip_suffix('h') {
45 (n, 3600)
46 } else if let Some(n) = s.strip_suffix('m') {
47 (n, 60)
48 } else if let Some(n) = s.strip_suffix('s') {
49 (n, 1)
50 } else {
51 return None;
52 };
53
54 let num: u64 = num_str.trim().parse().ok()?;
55 Some(Duration::from_secs(num * multiplier))
56}
57
58pub fn since_to_cutoff_ms(duration: Duration) -> i64 {
62 let now_ms = chrono::Utc::now().timestamp_millis();
63 now_ms - duration.as_millis() as i64
64}
65
66#[cfg(test)]
67#[allow(non_snake_case)]
68mod tests {
69 use super::*;
70
71 #[test]
72 fn parse_duration__seconds() {
73 assert_eq!(parse_duration("30s"), Some(Duration::from_secs(30)));
74 }
75
76 #[test]
77 fn parse_duration__minutes() {
78 assert_eq!(parse_duration("15m"), Some(Duration::from_secs(15 * 60)));
79 }
80
81 #[test]
82 fn parse_duration__hours() {
83 assert_eq!(parse_duration("2h"), Some(Duration::from_secs(2 * 3600)));
84 }
85
86 #[test]
87 fn parse_duration__days() {
88 assert_eq!(parse_duration("7d"), Some(Duration::from_secs(7 * 86400)));
89 }
90
91 #[test]
92 fn parse_duration__weeks() {
93 assert_eq!(
94 parse_duration("2w"),
95 Some(Duration::from_secs(2 * 7 * 86400))
96 );
97 }
98
99 #[test]
100 fn parse_duration__invalid_suffix() {
101 assert_eq!(parse_duration("10x"), None);
102 }
103
104 #[test]
105 fn parse_duration__invalid_number() {
106 assert_eq!(parse_duration("abch"), None);
107 }
108
109 #[test]
110 fn parse_duration__empty_string() {
111 assert_eq!(parse_duration(""), None);
112 }
113
114 #[test]
115 fn parse_duration__whitespace_handling() {
116 assert_eq!(parse_duration(" 5m "), Some(Duration::from_secs(5 * 60)));
117 }
118}