#![forbid(unsafe_code)]
const BYTE_UNITS: [&str; 9] = ["B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
pub fn format_bytes(bytes: u64) -> String {
if bytes < 1024 {
return format!("{bytes} B");
}
let mut value = bytes as f64;
let mut unit = 0;
while value >= 1024.0 && unit < BYTE_UNITS.len() - 1 {
value /= 1024.0;
unit += 1;
}
let rounded = (value * 10.0).round() / 10.0;
if rounded.fract() == 0.0 {
format!("{} {}", rounded as u64, BYTE_UNITS[unit])
} else {
format!("{rounded:.1} {}", BYTE_UNITS[unit])
}
}
pub fn format_duration(seconds: u64) -> String {
if seconds == 0 {
return "0s".to_string();
}
let days = seconds / 86_400;
let hours = (seconds % 86_400) / 3_600;
let minutes = (seconds % 3_600) / 60;
let secs = seconds % 60;
let mut parts = Vec::new();
if days > 0 {
parts.push(format!("{days}d"));
}
if hours > 0 {
parts.push(format!("{hours}h"));
}
if minutes > 0 {
parts.push(format!("{minutes}m"));
}
if secs > 0 {
parts.push(format!("{secs}s"));
}
parts.join(" ")
}
pub fn ordinal(n: i64) -> String {
let abs = n.unsigned_abs();
let last_two = abs % 100;
let last_one = abs % 10;
let suffix = if (11..=13).contains(&last_two) {
"th"
} else {
match last_one {
1 => "st",
2 => "nd",
3 => "rd",
_ => "th",
}
};
format!("{n}{suffix}")
}
pub fn pluralize(count: i64, singular: &str, plural: &str) -> String {
let word = if count.abs() == 1 { singular } else { plural };
format!("{count} {word}")
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn bytes() {
assert_eq!(format_bytes(0), "0 B");
assert_eq!(format_bytes(512), "512 B");
assert_eq!(format_bytes(1023), "1023 B");
assert_eq!(format_bytes(1024), "1 KB");
assert_eq!(format_bytes(1536), "1.5 KB");
assert_eq!(format_bytes(1_048_576), "1 MB");
assert_eq!(format_bytes(1_073_741_824), "1 GB");
}
#[test]
fn duration() {
assert_eq!(format_duration(0), "0s");
assert_eq!(format_duration(45), "45s");
assert_eq!(format_duration(90), "1m 30s");
assert_eq!(format_duration(3661), "1h 1m 1s");
assert_eq!(format_duration(90_061), "1d 1h 1m 1s");
}
#[test]
fn ordinals() {
assert_eq!(ordinal(1), "1st");
assert_eq!(ordinal(2), "2nd");
assert_eq!(ordinal(3), "3rd");
assert_eq!(ordinal(4), "4th");
assert_eq!(ordinal(11), "11th");
assert_eq!(ordinal(12), "12th");
assert_eq!(ordinal(13), "13th");
assert_eq!(ordinal(21), "21st");
assert_eq!(ordinal(101), "101st");
assert_eq!(ordinal(111), "111th");
}
#[test]
fn plurals() {
assert_eq!(pluralize(1, "file", "files"), "1 file");
assert_eq!(pluralize(3, "file", "files"), "3 files");
assert_eq!(pluralize(0, "file", "files"), "0 files");
assert_eq!(pluralize(2, "person", "people"), "2 people");
}
}