forge-guard 0.3.5

Pre-deployment smart contract auditing framework for Foundry
Documentation
//! Shared utilities — caching, formatting, and helper functions.

mod cache;
mod formatting;

pub use cache::Cache;
pub use formatting::*;

use crate::core::ForgeGuardError;

/// Get the number of available CPU cores for parallel execution.
pub fn cpu_count() -> usize {
    std::thread::available_parallelism()
        .map(|n| n.get())
        .unwrap_or(4)
}

/// Truncate a string to a maximum length with ellipsis.
pub fn truncate(s: &str, max_len: usize) -> String {
    if s.len() <= max_len {
        s.to_string()
    } else {
        format!("{}...", &s[..max_len.saturating_sub(3)])
    }
}

/// Read a file, trimming whitespace.
pub fn read_file_trimmed(path: &std::path::Path) -> Result<String, ForgeGuardError> {
    let content = std::fs::read_to_string(path)?;
    Ok(content.trim().to_string())
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_cpu_count() {
        let count = cpu_count();
        assert!(count >= 1);
    }

    #[test]
    fn test_truncate_short_string() {
        assert_eq!(truncate("hello", 10), "hello");
    }

    #[test]
    fn test_truncate_long_string() {
        let result = truncate("hello world this is long", 10);
        assert_eq!(result, "hello w...");
        assert_eq!(result.len(), 10);
    }

    #[test]
    fn test_truncate_exact() {
        assert_eq!(truncate("hello", 5), "hello");
    }

    #[test]
    fn test_truncate_empty() {
        assert_eq!(truncate("", 5), "");
    }

    #[test]
    fn test_format_duration_ms() {
        let result = format_duration(0.05);
        assert!(result.contains("50ms") || result.contains("0ms"));
    }

    #[test]
    fn test_format_duration_seconds() {
        let result = format_duration(5.5);
        assert_eq!(result, "5.5s");
    }

    #[test]
    fn test_format_duration_minutes() {
        let result = format_duration(125.0);
        assert_eq!(result, "2m 5s");
    }

    #[test]
    fn test_format_duration_hours() {
        let result = format_duration(3661.0);
        assert_eq!(result, "1h 1m");
    }

    #[test]
    fn test_format_number() {
        assert_eq!(format_number(0), "0");
        assert_eq!(format_number(100), "100");
        assert_eq!(format_number(1000), "1,000");
        assert_eq!(format_number(1000000), "1,000,000");
    }

    #[test]
    fn test_format_pct() {
        assert_eq!(format_pct(0.5), "50.0%");
        assert_eq!(format_pct(1.0), "100.0%");
        assert_eq!(format_pct(0.0), "0.0%");
    }

    #[test]
    fn test_format_bytes() {
        assert_eq!(format_bytes(0), "0 B");
        assert_eq!(format_bytes(500), "500 B");
        let kb = format_bytes(2048);
        assert!(kb.contains("KB") || kb.contains("2.00"));
        let mb = format_bytes(1048576);
        assert!(mb.contains("MB") || mb.contains("1.00"));
    }
}