veza/veza-common/src/utils/formatting.rs
2025-12-12 21:34:34 -05:00

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()
}