use super::funejson::Value; const UNITS: [(&str, i64); 4] = [("d", 86400), ("h", 3600), ("m", 60), ("s", 1)]; /// Seconds as "3h 12m". Starts at the largest non-zero unit and looks at /// `max_units` consecutive units from there, leaving out the zero ones, so /// 3605 s with two units is "1h", not "1h 0m" and not "1h 5s": the seconds /// are beyond the precision asked for. Truncates, never rounds up, so a /// duration is never shown longer than it was (86399 s is "23h", not "1d"). /// /// # Panics /// Panics on negative seconds or max_units outside 1..=4. pub fn format_duration(seconds: i64, max_units: i64) -> String { if seconds < 0 { panic!("seconds must not be negative, received {}", seconds); } if !(1..=4).contains(&max_units) { panic!("maxUnits must be 1 to 4, received {}", max_units); } if seconds == 0 { return "0s".to_string(); } let mut first = 0usize; while seconds / UNITS[first].1 == 0 { first += 1; } let mut parts: Vec = Vec::new(); let mut rest = seconds; for (i, (label, size)) in UNITS.iter().enumerate() { let n = rest / size; rest -= n * size; if i >= first && (i as i64) < first as i64 + max_units && n > 0 { parts.push(format!("{}{}", n, label)); } } parts.join(" ") } pub fn fune_vector(args: &[Value]) -> Value { let seconds = match &args[0] { Value::Int(i) => *i, Value::Float(f) => panic!("seconds must be a whole number, received {}", f), _ => panic!("seconds must be a whole number"), }; let max_units = match &args[1] { Value::Int(i) => *i, Value::Float(f) => panic!("maxUnits must be 1 to 4, received {}", f), _ => panic!("maxUnits must be 1 to 4"), }; Value::str(&format_duration(seconds, max_units)) }