132 lines
3.6 KiB
Rust
132 lines
3.6 KiB
Rust
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::<String>()
|
|
.split('-')
|
|
.filter(|s| !s.is_empty())
|
|
.collect::<Vec<&str>>()
|
|
.join("-")
|
|
}
|
|
|
|
/// Parse duration string (e.g., "1h30m", "45s")
|
|
pub fn parse_duration(duration_str: &str) -> Result<u64> {
|
|
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::<String>()
|
|
.trim()
|
|
.to_string()
|
|
}
|