#![cfg_attr(coverage_nightly, coverage(off))]
use std::path::Path;
use super::types::FileComplexityMetrics;
#[must_use]
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "path_exists")]
pub fn compute_complexity_cache_key(path: &Path, content: &[u8]) -> String {
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
let mut hasher = DefaultHasher::new();
content.hash(&mut hasher);
path.hash(&mut hasher);
format!("cx:{:x}", hasher.finish())
}
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "path_exists")]
pub async fn analyze_file_complexity_uncached(
path: &Path,
content: Option<&str>,
) -> anyhow::Result<FileComplexityMetrics> {
use anyhow::Context;
if let Some(supplied) = content {
let language = crate::cli::language_analyzer::Language::from_path(path);
return crate::cli::language_analyzer::analyze_with_heuristics(path, supplied, language)
.with_context(|| format!("Failed to analyze file complexity: {}", path.display()));
}
let file_content = std::fs::read_to_string(path)
.with_context(|| format!("Failed to read file: {}", path.display()))?;
crate::cli::language_analyzer::analyze_file_complexity(path, &file_content)
.await
.with_context(|| format!("Failed to analyze file complexity: {}", path.display()))
}
#[cfg(test)]
mod uncached_agreement_tests {
use super::*;
#[tokio::test]
async fn test_uncached_agrees_with_project_scan_analyzer() {
let dir = tempfile::TempDir::new().unwrap();
let file = dir.path().join("lib.rs");
let source = "pub fn add(a: i32, b: i32) -> i32 { a + b }\n\
\n\
pub fn complex(n: u32) -> u32 {\n\
\x20 let mut acc = 0;\n\
\x20 for i in 0..n {\n\
\x20 if i % 2 == 0 { acc += i; }\n\
\x20 else if i % 3 == 0 { acc += i * 2; }\n\
\x20 else if i % 5 == 0 { acc += i * 3; }\n\
\x20 else if i % 7 == 0 { acc += i * 4; }\n\
\x20 else { acc += 1; }\n\
\x20 }\n\
\x20 acc\n\
}\n";
std::fs::write(&file, source).unwrap();
let uncached = analyze_file_complexity_uncached(&file, None).await.unwrap();
let project_scan = crate::cli::language_analyzer::analyze_file_complexity(&file, source)
.await
.unwrap();
let pick = |m: &FileComplexityMetrics| {
m.functions
.iter()
.find(|f| f.name == "complex")
.map(|f| (f.metrics.cyclomatic, f.metrics.cognitive))
};
assert_eq!(
pick(&uncached),
pick(&project_scan),
"uncached analysis must not diverge from the project scan"
);
assert!(
pick(&uncached).is_some(),
"`complex` must be found: {:?}",
uncached.functions
);
}
}