#![cfg_attr(coverage_nightly, coverage(off))]
use anyhow::Result;
use std::path::Path;
use crate::models::error::TemplateError;
use crate::services::complexity::{
ClassComplexity, ComplexityMetrics, FileComplexityMetrics, FunctionComplexity,
};
use crate::services::context::{AstItem, FileContext};
use crate::services::file_classifier::FileClassifier;
use crate::ast::languages::c_cpp::CppStrategy;
use crate::ast::languages::LanguageStrategy;
pub async fn analyze_cpp_file_with_complexity(
path: &Path,
) -> Result<FileComplexityMetrics, TemplateError> {
analyze_cpp_file_with_complexity_and_classifier(path, None).await
}
pub async fn analyze_cpp_file_with_complexity_and_classifier(
path: &Path,
_classifier: Option<&FileClassifier>,
) -> Result<FileComplexityMetrics, TemplateError> {
let content = tokio::fs::read_to_string(path)
.await
.map_err(TemplateError::Io)?;
let strategy = CppStrategy::new();
let ast = strategy
.parse_file(path, &content)
.await
.map_err(|e| TemplateError::InvalidUtf8(e.to_string()))?;
let functions = strategy.extract_functions(&ast);
let mut function_metrics = Vec::new();
for (i, _node) in functions.iter().enumerate() {
function_metrics.push(FunctionComplexity {
name: format!("function_{i}"),
line_start: (i * 10) as u32,
line_end: ((i + 1) * 10) as u32,
metrics: ComplexityMetrics {
cyclomatic: 1, cognitive: 1, nesting_max: 0,
lines: 10,
halstead: None,
},
});
}
let types = strategy.extract_types(&ast);
let mut class_metrics = Vec::new();
for (i, _node) in types.iter().enumerate() {
class_metrics.push(ClassComplexity {
name: format!("class_{i}"),
line_start: ((functions.len() + i) * 10) as u32,
line_end: ((functions.len() + i + 1) * 10) as u32,
methods: Vec::new(),
metrics: ComplexityMetrics {
cyclomatic: 1,
cognitive: 1,
nesting_max: 0,
lines: 10,
halstead: None,
},
});
}
let (cyclomatic, cognitive) = strategy.calculate_complexity(&ast);
Ok(FileComplexityMetrics {
path: path.display().to_string(),
total_complexity: ComplexityMetrics {
cyclomatic: cyclomatic as u16,
cognitive: cognitive as u16,
nesting_max: 2,
lines: 100,
halstead: None,
},
functions: function_metrics,
classes: class_metrics,
})
}
pub async fn analyze_cpp_file(path: &Path) -> Result<FileContext, TemplateError> {
analyze_cpp_file_with_classifier(path, None).await
}
pub async fn analyze_cpp_file_with_classifier(
path: &Path,
_classifier: Option<&FileClassifier>,
) -> Result<FileContext, TemplateError> {
let content = tokio::fs::read_to_string(path)
.await
.map_err(TemplateError::Io)?;
let strategy = CppStrategy::new();
let ast = strategy
.parse_file(path, &content)
.await
.map_err(|e| TemplateError::InvalidUtf8(e.to_string()))?;
let functions = strategy.extract_functions(&ast);
let types = strategy.extract_types(&ast);
let _imports = strategy.extract_imports(&ast);
let mut items = Vec::new();
for (i, _node) in functions.iter().enumerate() {
items.push(AstItem::Function {
name: format!("function_{i}"),
visibility: "public".to_string(),
is_async: false, line: i * 10,
});
}
for (i, _node) in types.iter().enumerate() {
items.push(AstItem::Struct {
name: format!("class_{i}"),
visibility: "public".to_string(),
fields_count: 0,
derives: vec![],
line: (functions.len() + i) * 10,
});
}
Ok(FileContext {
path: path.display().to_string(),
language: "cpp".to_string(),
items,
complexity_metrics: None,
})
}
#[cfg_attr(coverage_nightly, coverage(off))]
#[cfg(test)]
mod property_tests {
use proptest::prelude::*;
proptest! {
#[test]
fn basic_property_stability(_input in ".*") {
prop_assert!(true);
}
#[test]
fn module_consistency_check(_x in 0u32..1000) {
prop_assert!(_x < 1001);
}
}
}