use std::time::{Duration, Instant};
use log::info;
#[derive(Debug)]
pub struct SimpleTimer {
start: Instant,
name: String,
}
impl SimpleTimer {
pub fn new(name: impl Into<String>) -> Self {
let name = name.into();
info!("开始计时: {}", name);
Self {
start: Instant::now(),
name,
}
}
pub fn elapsed(&self) -> Duration {
self.start.elapsed()
}
pub fn checkpoint(&self, message: &str) {
let elapsed = self.elapsed();
info!("{} - {}: {:.2}s", self.name, message, elapsed.as_secs_f64());
}
}
impl Drop for SimpleTimer {
fn drop(&mut self) {
let elapsed = self.elapsed();
info!("完成计时: {} - 总耗时: {:.2}s", self.name, elapsed.as_secs_f64());
}
}
pub fn format_bytes(bytes: u64) -> String {
const UNITS: &[&str] = &["B", "KB", "MB", "GB", "TB"];
const THRESHOLD: f64 = 1024.0;
if bytes == 0 {
return "0 B".to_string();
}
let mut size = bytes as f64;
let mut unit_index = 0;
while size >= THRESHOLD && unit_index < UNITS.len() - 1 {
size /= THRESHOLD;
unit_index += 1;
}
format!("{:.1} {}", size, UNITS[unit_index])
}
pub fn format_duration(duration: Duration) -> String {
let total_seconds = duration.as_secs();
let hours = total_seconds / 3600;
let minutes = (total_seconds % 3600) / 60;
let seconds = total_seconds % 60;
let millis = duration.subsec_millis();
if hours > 0 {
format!("{}h {}m {}s", hours, minutes, seconds)
} else if minutes > 0 {
format!("{}m {}s", minutes, seconds)
} else if seconds > 0 {
format!("{}.{:03}s", seconds, millis)
} else {
format!("{}ms", millis)
}
}
pub fn validate_range<T: PartialOrd + std::fmt::Display + Copy>(
value: T,
min: T,
max: T,
name: &str,
) -> crate::core::Result<()> {
if value < min || value > max {
return Err(crate::core::ColmapError::InvalidParameter(
format!("{} 必须在 [{}, {}] 范围内,当前值: {}", name, min, max, value)
));
}
Ok(())
}
pub fn validate_positive<T: PartialOrd + std::fmt::Display + Copy + Default>(
value: T,
name: &str,
) -> crate::core::Result<()> {
if value <= T::default() {
return Err(crate::core::ColmapError::InvalidParameter(
format!("{} 必须为正数,当前值: {}", name, value)
));
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
use std::thread;
#[test]
fn test_format_bytes() {
assert_eq!(format_bytes(0), "0 B");
assert_eq!(format_bytes(512), "512.0 B");
assert_eq!(format_bytes(1024), "1.0 KB");
assert_eq!(format_bytes(1536), "1.5 KB");
assert_eq!(format_bytes(1024 * 1024), "1.0 MB");
}
#[test]
fn test_format_duration() {
assert_eq!(format_duration(Duration::from_millis(500)), "500ms");
assert_eq!(format_duration(Duration::from_secs(1)), "1.000s");
assert_eq!(format_duration(Duration::from_secs(65)), "1m 5s");
assert_eq!(format_duration(Duration::from_secs(3665)), "1h 1m 5s");
}
#[test]
fn test_timer() {
let timer = SimpleTimer::new("测试计时器");
thread::sleep(Duration::from_millis(10));
assert!(timer.elapsed() >= Duration::from_millis(10));
}
#[test]
fn test_validation() {
assert!(validate_range(5, 0, 10, "test").is_ok());
assert!(validate_range(-1, 0, 10, "test").is_err());
assert!(validate_range(11, 0, 10, "test").is_err());
assert!(validate_positive(1, "test").is_ok());
assert!(validate_positive(0, "test").is_err());
assert!(validate_positive(-1, "test").is_err());
}
}