#![allow(unused)]
#![cfg_attr(coverage_nightly, coverage(off))]
use anyhow::Result;
use serde::{Deserialize, Serialize};
use std::path::Path;
pub mod entropy_calculator;
pub mod pattern_extractor;
pub mod violation_detector;
pub use entropy_calculator::{EntropyCalculator, EntropyMetrics, EntropyReport};
pub use pattern_extractor::{
AstPattern, Location, PatternCollection, PatternExtractor, PatternType,
};
pub use violation_detector::{ActionableViolation, PatternSummary, Severity, ViolationDetector};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EntropyConfig {
pub max_pattern_repetition: usize,
pub min_pattern_diversity: f64,
pub max_cross_file_similarity: f64,
pub max_inconsistency_score: f64,
pub min_severity: Severity,
pub pattern_types: Vec<PatternType>,
pub exclude_paths: Vec<String>,
}
impl Default for EntropyConfig {
fn default() -> Self {
Self {
max_pattern_repetition: 5,
min_pattern_diversity: 0.3,
max_cross_file_similarity: 0.7,
max_inconsistency_score: 0.8,
min_severity: Severity::Medium,
pattern_types: vec![
PatternType::ErrorHandling,
PatternType::DataValidation,
PatternType::ResourceManagement,
PatternType::ControlFlow,
PatternType::DataTransformation,
PatternType::ApiCall,
],
exclude_paths: vec!["tests/**".to_string(), "examples/**".to_string()],
}
}
}
impl EntropyConfig {
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "check_compliance")]
pub fn with_project_ignores(mut self, project_path: &std::path::Path) -> Self {
for ignore_file in &[".pmatignore", ".paimlignore"] {
let path = project_path.join(ignore_file);
if let Ok(content) = std::fs::read_to_string(&path) {
for line in content.lines() {
let trimmed = line.trim();
if !trimmed.is_empty() && !trimmed.starts_with('#') {
self.exclude_paths.push(trimmed.to_string());
}
}
}
}
self
}
}
pub struct EntropyAnalyzer {
config: EntropyConfig,
pattern_extractor: PatternExtractor,
violation_detector: ViolationDetector,
entropy_calculator: EntropyCalculator,
}
impl Default for EntropyAnalyzer {
fn default() -> Self {
Self::new()
}
}
impl EntropyAnalyzer {
#[must_use]
pub fn new() -> Self {
Self::with_config(EntropyConfig::default())
}
#[must_use]
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "check_compliance")]
pub fn with_config(config: EntropyConfig) -> Self {
Self {
config: config.clone(),
pattern_extractor: PatternExtractor::new(config.clone()),
violation_detector: ViolationDetector::new(config.clone()),
entropy_calculator: EntropyCalculator::new(config),
}
}
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "check_compliance")]
pub async fn analyze(&self, project_path: &Path) -> Result<EntropyReport> {
let patterns = self
.pattern_extractor
.extract_patterns(project_path)
.await?;
let entropy_metrics = self.entropy_calculator.calculate(&patterns)?;
let violations = self
.violation_detector
.detect_violations(&patterns, &entropy_metrics)?;
Ok(EntropyReport {
total_files_analyzed: patterns.file_count(),
actionable_violations: violations,
pattern_summary: patterns.summary(),
entropy_metrics,
})
}
}
#[cfg_attr(coverage_nightly, coverage(off))]
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_default_config() {
let config = EntropyConfig::default();
assert_eq!(config.max_pattern_repetition, 5);
assert_eq!(config.min_pattern_diversity, 0.3);
assert_eq!(config.max_cross_file_similarity, 0.7);
}
#[tokio::test]
async fn test_analyzer_creation() {
let analyzer = EntropyAnalyzer::new();
assert!(!analyzer.config.pattern_types.is_empty());
}
}
#[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);
}
}
}