Skip to main content

agent_berth/
duration.rs

1use std::time::Duration;
2
3use anyhow::{Context, Result, bail};
4
5pub fn parse_duration(input: &str) -> Result<Duration> {
6    let trimmed = input.trim();
7    if trimmed.is_empty() {
8        bail!("duration must not be empty");
9    }
10    let split = trimmed
11        .char_indices()
12        .find(|(_, ch)| !ch.is_ascii_digit() && *ch != '.')
13        .map(|(i, _)| i)
14        .unwrap_or(trimmed.len());
15    let (number, unit) = trimmed.split_at(split);
16    if number.is_empty() {
17        bail!("duration {input:?} is missing a number");
18    }
19    let value: f64 = number
20        .parse()
21        .with_context(|| format!("invalid duration number in {input:?}"))?;
22    if !value.is_finite() || value < 0.0 {
23        bail!("duration {input:?} must be a finite non-negative number");
24    }
25    let factor = match unit.trim().to_ascii_lowercase().as_str() {
26        "" | "s" | "sec" | "secs" | "second" | "seconds" => 1.0,
27        "m" | "min" | "mins" | "minute" | "minutes" => 60.0,
28        "h" | "hr" | "hrs" | "hour" | "hours" => 3600.0,
29        "d" | "day" | "days" => 86400.0,
30        other => bail!("unknown duration unit {other:?} in {input:?}"),
31    };
32    Ok(Duration::from_secs_f64(value * factor))
33}
34
35pub fn parse_idle(input: Option<&str>) -> Result<Option<Duration>> {
36    input.map(parse_duration).transpose()
37}
38
39#[cfg(test)]
40#[path = "duration_tests.rs"]
41mod tests;