pub fn parse_memory(s: &str) -> Option<i64> {
let trimmed = s.trim();
if trimmed.is_empty() {
return None;
}
if trimmed == "-1" {
return Some(-1);
}
let split_at = trimmed
.find(|c: char| !(c.is_ascii_digit() || c == '.' || c == '-'))
.unwrap_or(trimmed.len());
let (num_part, suffix) = trimmed.split_at(split_at);
let num_part = num_part.trim();
let suffix = suffix.trim().to_ascii_lowercase();
let num: f64 = num_part.parse().ok()?;
if num < 0.0 {
return None;
}
let multiplier: u64 = match suffix.as_str() {
"" | "b" => 1,
"k" | "kb" => 1024,
"m" | "mb" => 1024 * 1024,
"g" | "gb" => 1024 * 1024 * 1024,
"t" | "tb" => 1024_u64 * 1024 * 1024 * 1024,
_ => return None,
};
let bytes = num * multiplier as f64;
if bytes > i64::MAX as f64 {
return None;
}
Some(bytes as i64)
}
pub fn parse_cpus(s: &str) -> Option<i64> {
s.trim().parse::<f64>().ok().map(|f| (f * 1e9) as i64)
}
pub fn parse_duration_secs(s: &str) -> Option<u64> {
let trimmed = s.trim();
if trimmed.is_empty() {
return None;
}
let split_at = trimmed
.find(|c: char| !(c.is_ascii_digit() || c == '.'))
.unwrap_or(trimmed.len());
let (num_part, suffix) = trimmed.split_at(split_at);
let num: f64 = num_part.parse().ok()?;
let secs = match suffix.trim() {
"" | "s" => num,
"ms" => num / 1000.0,
"us" | "µs" => num / 1_000_000.0,
"ns" => num / 1_000_000_000.0,
"m" => num * 60.0,
"h" => num * 3600.0,
_ => return None,
};
Some(secs as u64)
}
pub fn parse_duration_nanos(s: &str) -> Option<i64> {
let trimmed = s.trim();
if trimmed.is_empty() {
return None;
}
let split_at = trimmed
.find(|c: char| !(c.is_ascii_digit() || c == '.'))
.unwrap_or(trimmed.len());
let (num_part, suffix) = trimmed.split_at(split_at);
let num: f64 = num_part.parse().ok()?;
let nanos = match suffix.trim() {
"" | "s" => num * 1e9,
"ms" => num * 1e6,
"us" | "µs" => num * 1e3,
"ns" => num,
"m" => num * 60.0 * 1e9,
"h" => num * 3600.0 * 1e9,
_ => return None,
};
Some(nanos as i64)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn memory_bare_bytes() {
assert_eq!(parse_memory("1024"), Some(1024));
}
#[test]
fn memory_suffix_b() {
assert_eq!(parse_memory("512b"), Some(512));
}
#[test]
fn memory_suffix_k() {
assert_eq!(parse_memory("4k"), Some(4 * 1024));
}
#[test]
fn memory_suffix_kb() {
assert_eq!(parse_memory("4kb"), Some(4 * 1024));
}
#[test]
fn memory_suffix_m() {
assert_eq!(parse_memory("128m"), Some(128 * 1024 * 1024));
}
#[test]
fn memory_suffix_mb() {
assert_eq!(parse_memory("128mb"), Some(128 * 1024 * 1024));
}
#[test]
fn memory_suffix_g() {
assert_eq!(parse_memory("1g"), Some(1024 * 1024 * 1024));
}
#[test]
fn memory_suffix_uppercase() {
assert_eq!(parse_memory("64M"), Some(64 * 1024 * 1024));
}
#[test]
fn memory_minus_one_special() {
assert_eq!(parse_memory("-1"), Some(-1));
}
#[test]
fn memory_empty_is_none() {
assert_eq!(parse_memory(""), None);
}
#[test]
fn memory_invalid_is_none() {
assert_eq!(parse_memory("abc"), None);
}
#[test]
fn memory_unknown_suffix_is_none() {
assert_eq!(parse_memory("100x"), None);
}
#[test]
fn cpus_integer() {
assert_eq!(parse_cpus("2"), Some(2_000_000_000));
}
#[test]
fn cpus_fraction() {
assert_eq!(parse_cpus("0.5"), Some(500_000_000));
}
#[test]
fn cpus_empty_is_none() {
assert_eq!(parse_cpus(""), None);
}
#[test]
fn duration_secs_plain_s() {
assert_eq!(parse_duration_secs("30s"), Some(30));
}
#[test]
fn duration_secs_minutes() {
assert_eq!(parse_duration_secs("2m"), Some(120));
}
#[test]
fn duration_secs_hours() {
assert_eq!(parse_duration_secs("1h"), Some(3600));
}
#[test]
fn duration_secs_milliseconds_truncates() {
assert_eq!(parse_duration_secs("500ms"), Some(0));
}
#[test]
fn duration_secs_bare_number() {
assert_eq!(parse_duration_secs("10"), Some(10));
}
#[test]
fn duration_secs_empty_is_none() {
assert_eq!(parse_duration_secs(""), None);
}
#[test]
fn duration_secs_unknown_suffix_is_none() {
assert_eq!(parse_duration_secs("5d"), None);
}
#[test]
fn duration_nanos_seconds() {
assert_eq!(parse_duration_nanos("1s"), Some(1_000_000_000));
}
#[test]
fn duration_nanos_milliseconds() {
assert_eq!(parse_duration_nanos("200ms"), Some(200_000_000));
}
#[test]
fn duration_nanos_minutes() {
assert_eq!(parse_duration_nanos("1m"), Some(60 * 1_000_000_000));
}
#[test]
fn duration_nanos_nanoseconds() {
assert_eq!(parse_duration_nanos("500ns"), Some(500));
}
}