use std::time::{Duration, Instant};
#[derive(Debug, Clone)]
pub struct Timer {
start: Instant,
}
impl Timer {
pub fn start() -> Self {
Self {
start: Instant::now(),
}
}
pub fn elapsed(&self) -> Duration {
self.start.elapsed()
}
#[allow(dead_code)]
pub fn elapsed_formatted(&self) -> String {
format_duration(self.elapsed())
}
}
impl Default for Timer {
fn default() -> Self {
Self::start()
}
}
pub fn format_duration(duration: Duration) -> String {
let millis = duration.as_millis();
if millis == 0 {
"< 1ms".to_string()
} else if millis >= 1000 {
let secs = duration.as_secs_f64();
format!("{:.2}s", secs)
} else {
format!("{}ms", millis)
}
}
#[derive(Debug, Clone)]
pub struct CategoryTiming {
pub name: String,
#[allow(dead_code)]
pub rule_count: usize,
pub findings_count: usize,
pub duration: Duration,
}
impl CategoryTiming {
pub fn new(name: &str, rule_count: usize, findings_count: usize, duration: Duration) -> Self {
Self {
name: name.to_string(),
rule_count,
findings_count,
duration,
}
}
pub fn duration_formatted(&self) -> String {
format_duration(self.duration)
}
}
#[derive(Debug, Clone, Default)]
pub struct AuditTiming {
pub categories: Vec<CategoryTiming>,
pub total_duration: Duration,
}
impl AuditTiming {
pub fn new() -> Self {
Self {
categories: Vec::new(),
total_duration: Duration::ZERO,
}
}
pub fn add_category(&mut self, timing: CategoryTiming) {
self.categories.push(timing);
}
pub fn set_total_duration(&mut self, duration: Duration) {
self.total_duration = duration;
}
pub fn total_duration_formatted(&self) -> String {
format_duration(self.total_duration)
}
pub fn categories(&self) -> &[CategoryTiming] {
&self.categories
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_format_duration_less_than_1ms() {
let duration = Duration::from_micros(500);
assert_eq!(format_duration(duration), "< 1ms");
let duration = Duration::from_nanos(100);
assert_eq!(format_duration(duration), "< 1ms");
let duration = Duration::ZERO;
assert_eq!(format_duration(duration), "< 1ms");
}
#[test]
fn test_format_duration_milliseconds() {
let duration = Duration::from_millis(1);
assert_eq!(format_duration(duration), "1ms");
let duration = Duration::from_millis(456);
assert_eq!(format_duration(duration), "456ms");
let duration = Duration::from_millis(999);
assert_eq!(format_duration(duration), "999ms");
}
#[test]
fn test_format_duration_seconds() {
let duration = Duration::from_millis(1000);
assert_eq!(format_duration(duration), "1.00s");
let duration = Duration::from_millis(1234);
assert_eq!(format_duration(duration), "1.23s");
let duration = Duration::from_secs(2);
assert_eq!(format_duration(duration), "2.00s");
let duration = Duration::from_secs_f64(3.456);
assert_eq!(format_duration(duration), "3.46s");
let duration = Duration::from_secs(60);
assert_eq!(format_duration(duration), "60.00s");
}
#[test]
fn test_timer_basic() {
let timer = Timer::start();
std::thread::sleep(Duration::from_millis(10));
let elapsed = timer.elapsed();
assert!(elapsed >= Duration::from_millis(10));
}
#[test]
fn test_timer_elapsed_formatted() {
let timer = Timer::start();
std::thread::sleep(Duration::from_millis(50));
let formatted = timer.elapsed_formatted();
assert!(
formatted.ends_with("ms") || formatted.ends_with('s'),
"Expected formatted duration, got: {}",
formatted
);
}
#[test]
fn test_timer_default() {
let timer = Timer::default();
let elapsed = timer.elapsed();
assert!(elapsed < Duration::from_secs(1));
}
#[test]
fn test_category_timing() {
let duration = Duration::from_millis(123);
let timing = CategoryTiming::new("secrets", 12, 3, duration);
assert_eq!(timing.name, "secrets");
assert_eq!(timing.rule_count, 12);
assert_eq!(timing.findings_count, 3);
assert_eq!(timing.duration, duration);
assert_eq!(timing.duration_formatted(), "123ms");
}
#[test]
fn test_audit_timing() {
let mut audit_timing = AuditTiming::new();
audit_timing.add_category(CategoryTiming::new(
"secrets",
10,
2,
Duration::from_millis(45),
));
audit_timing.add_category(CategoryTiming::new(
"files",
8,
1,
Duration::from_millis(23),
));
audit_timing.set_total_duration(Duration::from_millis(1234));
assert_eq!(audit_timing.categories().len(), 2);
assert_eq!(audit_timing.total_duration_formatted(), "1.23s");
}
#[test]
fn test_audit_timing_default() {
let timing = AuditTiming::default();
assert!(timing.categories.is_empty());
assert_eq!(timing.total_duration, Duration::ZERO);
}
}