use chrono::Duration; pub fn sanitize_filename(s: &str) -> String { s.split('.') .map(sanitize_filename_part) .collect::>() .join(".") } pub fn sanitize_filename_part(s: &str) -> String { s.chars() .map(|c| { if c.is_ascii_alphanumeric() { c.to_lowercase().collect::() } else { " ".to_string() } }) .collect::() .split_whitespace() .collect::>() .join("-") } pub fn pretty_print_duration(d: Duration) -> String { if d.num_days() > 0 { let plural = if d.num_days() > 1 { "s" } else { "" }; let remaining_hours = d.num_hours() - d.num_days() * 24; let formatted_hours = if remaining_hours > 0 { format!(" {remaining_hours} h") } else { "".to_string() }; format!("{} day{}{}", d.num_days(), plural, formatted_hours) } else if d.num_hours() > 0 { let remaining_minutes = d.num_minutes() - d.num_hours() * 60; let formatted_minutes = if remaining_minutes > 0 { format!(" {remaining_minutes} min") } else { "".to_string() }; format!("{} h{}", d.num_hours(), formatted_minutes) } else if d.num_minutes() > 0 { let remaining_seconds = d.num_seconds() - d.num_minutes() * 60; let formatted_seconds = if remaining_seconds > 0 { format!(" {remaining_seconds} s") } else { "".to_string() }; format!("{} min{}", d.num_minutes(), formatted_seconds) } else { format!("{} s", d.num_seconds()) } } pub fn pretty_print_bytes(bytes: usize) -> String { let bytes_f32 = bytes as f32; let ko = bytes_f32 / 1024.0; let mo = ko / 1024.0; let go = mo / 1024.0; if go >= 1.0 { format!("{:.1} G", go) } else if mo >= 1.0 { format!("{:.1} M", mo) } else if ko >= 1.0 { format!("{:.1} K", ko) } else { format!("{} B", bytes) } } #[cfg(test)] mod tests { use super::*; #[test] fn test_sanitize_filename() { assert_eq!(sanitize_filename(""), ""); assert_eq!(sanitize_filename("foo bar 123"), "foo-bar-123"); assert_eq!(sanitize_filename("foo bar.123"), "foo-bar.123"); assert_eq!(sanitize_filename("foo ( test+2 ).xml"), "foo-test-2.xml"); } #[test] fn test_sanitize_filename_part() { assert_eq!(sanitize_filename_part(""), ""); assert_eq!(sanitize_filename_part("foo123BAZ"), "foo123baz"); assert_eq!(sanitize_filename_part("foo-123-BAZ"), "foo-123-baz"); assert_eq!(sanitize_filename_part("[()] */+-!;?<'> ?:"), ""); assert_eq!(sanitize_filename_part("foo [bar] -- BAZ3"), "foo-bar-baz3"); } #[test] fn test_pretty_print_duration() { assert_eq!( pretty_print_duration(Duration::days(2)), "2 days".to_string() ); assert_eq!( pretty_print_duration(Duration::hours(30)), "1 day 6 h".to_string() ); assert_eq!( pretty_print_duration(Duration::days(1)), "1 day".to_string() ); assert_eq!( pretty_print_duration(Duration::hours(15)), "15 h".to_string() ); assert_eq!( pretty_print_duration(Duration::minutes(70)), "1 h 10 min".to_string() ); assert_eq!( pretty_print_duration(Duration::minutes(44)), "44 min".to_string() ); assert_eq!( pretty_print_duration(Duration::seconds(100)), "1 min 40 s".to_string() ); assert_eq!( pretty_print_duration(Duration::seconds(7)), "7 s".to_string() ); assert_eq!(pretty_print_duration(Duration::zero()), "0 s".to_string()); } #[test] fn test_pretty_print_bytes() { assert_eq!(pretty_print_bytes(0), "0 B"); assert_eq!(pretty_print_bytes(10), "10 B"); assert_eq!(pretty_print_bytes(1024), "1.0 K"); assert_eq!(pretty_print_bytes(1100), "1.1 K"); assert_eq!(pretty_print_bytes(54 * 1024), "54.0 K"); assert_eq!(pretty_print_bytes(1024 * 1024), "1.0 M"); assert_eq!(pretty_print_bytes(1250 * 1024), "1.2 M"); assert_eq!(pretty_print_bytes(79 * 1024 * 1024), "79.0 M"); assert_eq!(pretty_print_bytes(1024 * 1024 * 1024), "1.0 G"); assert_eq!(pretty_print_bytes(1300 * 1024 * 1024), "1.3 G"); assert_eq!(pretty_print_bytes(245 * 1024 * 1024 * 1024), "245.0 G"); } }