pub fn format_time(seconds: f64) -> String { if !seconds.is_finite() || seconds < 0.0 { return "00:00".to_string(); } let total_secs = seconds as u64; let hours = total_secs / 3600; let minutes = (total_secs % 3600) / 60; let secs = total_secs % 60; if hours > 0 { format!("{hours:02}:{minutes:02}:{secs:02}") } else { format!("{minutes:02}:{secs:02}") } } pub fn format_duration_ns(ns: u64) -> String { format_time((ns / 1_000_000_000) as f64) } pub fn format_duration_human(ns: u64) -> String { let total_mins = ns / 60_000_000_000; let hours = total_mins / 60; let mins = total_mins % 60; match (hours, mins) { (0, 0) => "< 1 min".to_string(), (0, m) => format!("{m} min"), (h, 0) => format!("{h} hr"), (h, m) => format!("{h} hr {m} min"), } } #[cfg(any(test, all(target_family = "wasm", target_os = "unknown")))] pub fn seek_position_from_click(click_x: f64, rect_left: f64, rect_width: f64, duration: f64) -> f64 { if rect_width <= 0.0 || !duration.is_finite() || duration <= 0.0 { return 0.0; } let ratio = ((click_x - rect_left) / rect_width).clamp(0.0, 1.0); ratio * duration } #[cfg(test)] mod tests { use super::*; #[test] fn zero_seconds() { assert_eq!(format_time(0.0), "00:00"); } #[test] fn sub_minute() { assert_eq!(format_time(45.0), "00:45"); } #[test] fn minutes_and_seconds() { assert_eq!(format_time(125.0), "02:05"); } #[test] fn hours() { assert_eq!(format_time(3661.0), "01:01:01"); } #[test] fn negative_returns_zero() { assert_eq!(format_time(-5.0), "00:00"); } #[test] fn nan_returns_zero() { assert_eq!(format_time(f64::NAN), "00:00"); } #[test] fn infinity_returns_zero() { assert_eq!(format_time(f64::INFINITY), "00:00"); } #[test] fn duration_ns_one_minute() { assert_eq!(format_duration_ns(60_000_000_000), "01:00"); } #[test] fn duration_ns_ninety_minutes() { assert_eq!(format_duration_ns(5_400_000_000_000), "01:30:00"); } #[test] fn human_sub_minute() { assert_eq!(format_duration_human(30_000_000_000), "< 1 min"); } #[test] fn human_minutes_only() { assert_eq!(format_duration_human(1_451_000_000_000), "24 min"); } #[test] fn human_hours_and_minutes() { assert_eq!(format_duration_human(5_580_000_000_000), "1 hr 33 min"); } #[test] fn human_exact_hours() { assert_eq!(format_duration_human(7_200_000_000_000), "2 hr"); } #[test] fn seek_midpoint() { let t = seek_position_from_click(150.0, 100.0, 200.0, 60.0); assert!((t - 15.0).abs() < 0.001); } #[test] fn seek_clamps_left() { let t = seek_position_from_click(50.0, 100.0, 200.0, 60.0); assert_eq!(t, 0.0); } #[test] fn seek_clamps_right() { let t = seek_position_from_click(350.0, 100.0, 200.0, 60.0); assert_eq!(t, 60.0); } #[test] fn seek_zero_width() { assert_eq!(seek_position_from_click(100.0, 100.0, 0.0, 60.0), 0.0); } #[test] fn seek_nan_duration() { assert_eq!(seek_position_from_click(150.0, 100.0, 200.0, f64::NAN), 0.0); } }