Add TTL expiry for transient facts

This commit is contained in:
Agent Zero
2026-03-24 03:20:10 +00:00
parent 1314015479
commit 5d5c042dd1
12 changed files with 241 additions and 15 deletions

49
src/ttl.rs Normal file
View File

@@ -0,0 +1,49 @@
use anyhow::{Result, anyhow};
use chrono::{DateTime, Duration, Utc};
pub fn parse_ttl_spec(ttl: &str) -> Result<Duration> {
let ttl = ttl.trim();
if ttl.is_empty() {
return Err(anyhow!("ttl must not be empty"));
}
let (value, multiplier_seconds) = match ttl.chars().last() {
Some('s') | Some('S') => (&ttl[..ttl.len() - 1], 1i64),
Some('m') | Some('M') => (&ttl[..ttl.len() - 1], 60i64),
Some('h') | Some('H') => (&ttl[..ttl.len() - 1], 60i64 * 60),
Some('d') | Some('D') => (&ttl[..ttl.len() - 1], 60i64 * 60 * 24),
Some('w') | Some('W') => (&ttl[..ttl.len() - 1], 60i64 * 60 * 24 * 7),
_ => {
return Err(anyhow!(
"invalid ttl '{ttl}'. Use a positive duration like 30s, 15m, 1h, 7d, or 2w"
));
}
};
let value: i64 = value
.trim()
.parse()
.map_err(|_| anyhow!("invalid ttl '{ttl}'. Duration value must be a positive integer"))?;
if value <= 0 {
return Err(anyhow!("invalid ttl '{ttl}'. Duration value must be greater than zero"));
}
let total_seconds = value
.checked_mul(multiplier_seconds)
.ok_or_else(|| anyhow!("invalid ttl '{ttl}'. Duration is too large"))?;
Ok(Duration::seconds(total_seconds))
}
pub fn expires_at_from_ttl(ttl: Option<&str>) -> Result<Option<DateTime<Utc>>> {
match ttl {
Some(ttl) => {
let duration = parse_ttl_spec(ttl)?;
Utc::now()
.checked_add_signed(duration)
.map(Some)
.ok_or_else(|| anyhow!("ttl '{ttl}' overflows supported timestamp range"))
}
None => Ok(None),
}
}