use crate::error::{CommonResult as Result, CommonError as Error}; /// Format duration in human-readable format pub fn format_duration(seconds: u32) -> String { let hours = seconds / 3600; let minutes = (seconds % 3600) / 60; let secs = seconds % 60; if hours > 0 { format!("{}:{:02}:{:02}", hours, minutes, secs) } else { format!("{}:{:02}", minutes, secs) } } /// Format file size in human-readable format pub fn format_file_size(bytes: u64) -> String { const UNITS: &[&str] = &["B", "KB", "MB", "GB", "TB"]; const THRESHOLD: u64 = 1024; if bytes == 0 { return "0 B".to_string(); } let mut size = bytes as f64; let mut unit_index = 0; while size >= THRESHOLD as f64 && unit_index < UNITS.len() - 1 { size /= THRESHOLD as f64; unit_index += 1; } if unit_index == 0 { format!("{} {}", bytes, UNITS[unit_index]) } else { format!("{:.1} {}", size, UNITS[unit_index]) } } /// Format number with thousands separator pub fn format_number(n: u64) -> String { let s = n.to_string(); let mut result = String::new(); let mut count = 0; for c in s.chars().rev() { if count > 0 && count % 3 == 0 { result.push(','); } result.push(c); count += 1; } result.chars().rev().collect() } /// Truncate string to specified length pub fn truncate_string(s: &str, max_length: usize) -> String { if s.len() <= max_length { s.to_string() } else { format!("{}...", &s[..max_length.saturating_sub(3)]) } } /// Check if string is empty or whitespace only pub fn is_empty_or_whitespace(s: &str) -> bool { s.trim().is_empty() } /// Convert string to slug pub fn to_slug(input: &str) -> String { input .to_lowercase() .chars() .map(|c| match c { ' ' => '-', c if c.is_alphanumeric() => c, _ => '-', }) .collect::() .split('-') .filter(|s| !s.is_empty()) .collect::>() .join("-") } /// Parse duration string (e.g., "1h30m", "45s") pub fn parse_duration(duration_str: &str) -> Result { let mut total_seconds = 0u64; let mut current_number = String::new(); for c in duration_str.chars() { if c.is_numeric() { current_number.push(c); } else { if !current_number.is_empty() { let number: u64 = current_number.parse() .map_err(|_| Error::ValidationError(format!("Invalid duration: {}", duration_str)))?; match c { 's' => total_seconds += number, 'm' => total_seconds += number * 60, 'h' => total_seconds += number * 3600, 'd' => total_seconds += number * 86400, _ => return Err(Error::ValidationError(format!("Invalid duration unit: {}", c))), } current_number.clear(); } } } // Handle case where duration ends with a number (assume seconds) if !current_number.is_empty() { let number: u64 = current_number.parse() .map_err(|_| Error::ValidationError(format!("Invalid duration: {}", duration_str)))?; total_seconds += number; } Ok(total_seconds) } /// Sanitize string input pub fn sanitize_string(input: &str) -> String { input .chars() .filter(|c| !c.is_control() || *c == '\n' || *c == '\r' || *c == '\t') .collect::() .trim() .to_string() }