Skip to main content

kaish_kernel/
duration.rs

1//! Duration parsing for shell-style time strings.
2//!
3//! Used by the `timeout` builtin and `scatter --timeout` to parse durations
4//! like `30`, `30s`, `500ms`, `5m`, `1h`.
5
6use std::time::Duration;
7
8/// Parse a duration string: `30` (seconds), `30s`, `500ms`, `5m`, `1h`.
9///
10/// Returns `None` for invalid input (negative, unrecognized suffix, non-numeric).
11pub fn parse_duration(s: &str) -> Option<Duration> {
12    let s = s.trim();
13
14    if let Ok(secs) = s.parse::<f64>() {
15        return if secs >= 0.0 {
16            Some(Duration::from_secs_f64(secs))
17        } else {
18            None
19        };
20    }
21
22    if let Some(num) = s.strip_suffix("ms") {
23        let ms: u64 = num.trim().parse().ok()?;
24        return Some(Duration::from_millis(ms));
25    }
26    if let Some(num) = s.strip_suffix('s') {
27        let secs: f64 = num.trim().parse().ok()?;
28        return if secs >= 0.0 {
29            Some(Duration::from_secs_f64(secs))
30        } else {
31            None
32        };
33    }
34    if let Some(num) = s.strip_suffix('m') {
35        let mins: f64 = num.trim().parse().ok()?;
36        return if mins >= 0.0 {
37            Some(Duration::from_secs_f64(mins * 60.0))
38        } else {
39            None
40        };
41    }
42    if let Some(num) = s.strip_suffix('h') {
43        let hours: f64 = num.trim().parse().ok()?;
44        return if hours >= 0.0 {
45            Some(Duration::from_secs_f64(hours * 3600.0))
46        } else {
47            None
48        };
49    }
50
51    None
52}
53
54#[cfg(test)]
55mod tests {
56    use super::*;
57
58    #[test]
59    fn seconds() {
60        assert_eq!(parse_duration("30"), Some(Duration::from_secs(30)));
61        assert_eq!(parse_duration("0"), Some(Duration::from_secs(0)));
62        assert_eq!(parse_duration("1.5"), Some(Duration::from_secs_f64(1.5)));
63    }
64
65    #[test]
66    fn suffixes() {
67        assert_eq!(parse_duration("500ms"), Some(Duration::from_millis(500)));
68        assert_eq!(parse_duration("5s"), Some(Duration::from_secs(5)));
69        assert_eq!(parse_duration("2m"), Some(Duration::from_secs(120)));
70        assert_eq!(parse_duration("1h"), Some(Duration::from_secs(3600)));
71    }
72
73    #[test]
74    fn invalid() {
75        assert_eq!(parse_duration("abc"), None);
76        assert_eq!(parse_duration(""), None);
77        assert_eq!(parse_duration("-5"), None);
78        assert_eq!(parse_duration("5x"), None);
79    }
80}