use serde::{Deserialize, Serialize};
use std::collections::{HashMap, HashSet};
use std::path::PathBuf;
pub mod adaptive;
pub mod alerts;
pub mod analyzer_ast;
pub mod analyzer_simple;
pub mod config;
pub mod diagnostics;
pub mod export;
pub mod formatters;
pub mod language_simple;
pub mod metrics_aggregator;
pub mod profiler;
pub mod resource_control;
pub mod scheduler;
pub mod storage;
pub mod storage_backend;
pub mod web_dashboard;
#[cfg(test)]
mod integration_test_sprint30;
pub use adaptive::{
AdaptiveConfig, AdaptiveThresholdFactory, AdaptiveThresholdManager, CurrentThresholds,
PerformanceSample, PerformanceStatistics, PerformanceTrend, ThresholdAdjustment,
};
pub use analyzer_ast::TdgAnalyzerAst as TdgAnalyzer;
pub use analyzer_simple::TdgAnalyzer as TdgAnalyzerSimple;
pub use config::TdgConfig;
pub use diagnostics::{
AdaptiveDiagnostics, HealthStatus, ResourceDiagnostics, SchedulerDiagnostics,
StorageDiagnostics, SystemDiagnostics,
};
pub use formatters::{format_human, format_json, format_markdown};
pub use language_simple::{Language, LanguageRules};
pub use resource_control::{
OperationPriority, PlatformResourceController, ResourceAction, ResourceAllocation,
ResourceControllerFactory, ResourceEnforcementStats, ResourceLimits, ResourcePressure,
ResourceUsage,
};
pub use scheduler::{
OperationType, ScheduleError, ScheduleGuard, SchedulePermit, SchedulerFactory,
SchedulingStatistics, SimpleFairScheduler,
};
pub use storage::{
AnalysisMetadata, ComponentScores, FileIdentity, FullTdgRecord, HotCacheEntry,
SemanticSignature, StorageStatistics, TieredStorageFactory, TieredStore,
};
pub use storage_backend::{
InMemoryBackend, SledBackend, StorageBackend, StorageBackendFactory, StorageBackendType,
StorageConfig,
};
pub use web_dashboard::{
create_dashboard_router, start_dashboard_server, DashboardState,
HealthStatus as DashboardHealthStatus, PerformanceMetrics as DashboardPerformanceMetrics,
StorageMetrics, SystemMetrics,
};
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct TdgScore {
pub structural_complexity: f32,
pub semantic_complexity: f32,
pub duplication_ratio: f32,
pub coupling_score: f32,
pub doc_coverage: f32,
pub consistency_score: f32,
pub entropy_score: f32, pub total: f32,
pub grade: Grade,
pub confidence: f32,
pub language: Language,
pub file_path: Option<PathBuf>,
pub penalties_applied: Vec<PenaltyAttribution>,
}
impl Default for TdgScore {
fn default() -> Self {
Self {
structural_complexity: 25.0,
semantic_complexity: 20.0,
duplication_ratio: 20.0,
coupling_score: 15.0,
doc_coverage: 10.0,
consistency_score: 10.0,
entropy_score: 0.0, total: 100.0,
grade: Grade::APLus,
confidence: 1.0,
language: Language::Unknown,
file_path: None,
penalties_applied: Vec::new(),
}
}
}
impl TdgScore {
pub fn calculate_total(&mut self) {
self.total = self.structural_complexity
+ self.semantic_complexity
+ self.duplication_ratio
+ self.coupling_score
+ self.doc_coverage
+ self.consistency_score
+ self.entropy_score;
self.grade = Grade::from_score(self.total);
}
pub fn set_metric(&mut self, category: MetricCategory, value: f32) {
match category {
MetricCategory::StructuralComplexity => self.structural_complexity = value,
MetricCategory::SemanticComplexity => self.semantic_complexity = value,
MetricCategory::Duplication => self.duplication_ratio = value,
MetricCategory::Coupling => self.coupling_score = value,
MetricCategory::Documentation => self.doc_coverage = value,
MetricCategory::Consistency => self.consistency_score = value,
}
}
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
pub enum Grade {
APLus,
A,
AMinus,
BPlus,
B,
BMinus,
CPlus,
C,
CMinus,
D,
F,
}
impl Grade {
#[must_use]
pub fn from_score(score: f32) -> Self {
match score {
s if s >= 95.0 => Grade::APLus,
s if s >= 90.0 => Grade::A,
s if s >= 85.0 => Grade::AMinus,
s if s >= 80.0 => Grade::BPlus,
s if s >= 75.0 => Grade::B,
s if s >= 70.0 => Grade::BMinus,
s if s >= 65.0 => Grade::CPlus,
s if s >= 60.0 => Grade::C,
s if s >= 55.0 => Grade::CMinus,
s if s >= 50.0 => Grade::D,
_ => Grade::F,
}
}
}
impl std::fmt::Display for Grade {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Grade::APLus => write!(f, "A+"),
Grade::A => write!(f, "A"),
Grade::AMinus => write!(f, "A-"),
Grade::BPlus => write!(f, "B+"),
Grade::B => write!(f, "B"),
Grade::BMinus => write!(f, "B-"),
Grade::CPlus => write!(f, "C+"),
Grade::C => write!(f, "C"),
Grade::CMinus => write!(f, "C-"),
Grade::D => write!(f, "D"),
Grade::F => write!(f, "F"),
}
}
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Hash)]
pub enum MetricCategory {
StructuralComplexity,
SemanticComplexity,
Duplication,
Coupling,
Documentation,
Consistency,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct PenaltyAttribution {
pub source_metric: MetricCategory,
pub amount: f32,
pub applied_to: HashSet<MetricCategory>,
pub issue: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ProjectScore {
pub files: Vec<TdgScore>,
pub average_score: f32,
pub average_grade: Grade,
pub total_files: usize,
pub language_distribution: HashMap<Language, usize>,
}
impl ProjectScore {
#[must_use]
pub fn aggregate(scores: Vec<TdgScore>) -> Self {
let total_files = scores.len();
let average_score = if total_files > 0 {
scores.iter().map(|s| s.total).sum::<f32>() / total_files as f32
} else {
0.0
};
let mut language_distribution = HashMap::new();
for score in &scores {
*language_distribution.entry(score.language).or_insert(0) += 1;
}
Self {
files: scores,
average_score,
average_grade: Grade::from_score(average_score),
total_files,
language_distribution,
}
}
#[must_use]
pub fn average(&self) -> TdgScore {
if self.files.is_empty() {
return TdgScore::default();
}
let mut avg = TdgScore::default();
let count = self.files.len() as f32;
avg.structural_complexity = self
.files
.iter()
.map(|s| s.structural_complexity)
.sum::<f32>()
/ count;
avg.semantic_complexity = self
.files
.iter()
.map(|s| s.semantic_complexity)
.sum::<f32>()
/ count;
avg.duplication_ratio = self.files.iter().map(|s| s.duplication_ratio).sum::<f32>() / count;
avg.coupling_score = self.files.iter().map(|s| s.coupling_score).sum::<f32>() / count;
avg.doc_coverage = self.files.iter().map(|s| s.doc_coverage).sum::<f32>() / count;
avg.consistency_score = self.files.iter().map(|s| s.consistency_score).sum::<f32>() / count;
avg.entropy_score = self.files.iter().map(|s| s.entropy_score).sum::<f32>() / count;
avg.confidence = self.files.iter().map(|s| s.confidence).sum::<f32>() / count;
avg.calculate_total();
avg
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Comparison {
pub source1: TdgScore,
pub source2: TdgScore,
pub delta: f32,
pub improvement_percentage: f32,
pub winner: String,
pub improvements: Vec<String>,
pub regressions: Vec<String>,
}
impl Comparison {
#[must_use]
pub fn new(source1: TdgScore, source2: TdgScore) -> Self {
let delta = source2.total - source1.total;
let improvement_percentage = if source1.total > 0.0 {
(delta / source1.total) * 100.0
} else {
0.0
};
let winner = if source2.total > source1.total {
source2
.file_path
.as_ref().map_or_else(|| "source2".to_string(), |p| p.display().to_string())
} else {
source1
.file_path
.as_ref().map_or_else(|| "source1".to_string(), |p| p.display().to_string())
};
let mut improvements = Vec::new();
let mut regressions = Vec::new();
if source2.structural_complexity > source1.structural_complexity {
improvements.push(format!(
"Structural complexity improved by {:.1}",
source2.structural_complexity - source1.structural_complexity
));
} else if source2.structural_complexity < source1.structural_complexity {
regressions.push(format!(
"Structural complexity degraded by {:.1}",
source1.structural_complexity - source2.structural_complexity
));
}
if source2.semantic_complexity > source1.semantic_complexity {
improvements.push(format!(
"Semantic complexity improved by {:.1}",
source2.semantic_complexity - source1.semantic_complexity
));
} else if source2.semantic_complexity < source1.semantic_complexity {
regressions.push(format!(
"Semantic complexity degraded by {:.1}",
source1.semantic_complexity - source2.semantic_complexity
));
}
if source2.duplication_ratio > source1.duplication_ratio {
improvements.push(format!(
"Code duplication reduced by {:.1}",
source2.duplication_ratio - source1.duplication_ratio
));
} else if source2.duplication_ratio < source1.duplication_ratio {
regressions.push(format!(
"Code duplication increased by {:.1}",
source1.duplication_ratio - source2.duplication_ratio
));
}
if source2.doc_coverage > source1.doc_coverage {
improvements.push(format!(
"Documentation coverage improved by {:.1}",
source2.doc_coverage - source1.doc_coverage
));
} else if source2.doc_coverage < source1.doc_coverage {
regressions.push(format!(
"Documentation coverage decreased by {:.1}",
source1.doc_coverage - source2.doc_coverage
));
}
Self {
source1,
source2,
delta,
improvement_percentage,
winner,
improvements,
regressions,
}
}
}
pub struct PenaltyTracker {
applied: HashMap<String, PenaltyAttribution>,
}
impl Default for PenaltyTracker {
fn default() -> Self {
Self::new()
}
}
impl PenaltyTracker {
#[must_use]
pub fn new() -> Self {
Self {
applied: HashMap::new(),
}
}
pub fn apply(
&mut self,
issue_id: String,
category: MetricCategory,
amount: f32,
issue: String,
) -> Option<f32> {
if self.applied.contains_key(&issue_id) {
return None;
}
self.applied.insert(
issue_id,
PenaltyAttribution {
source_metric: category,
amount,
applied_to: HashSet::from([category]),
issue,
},
);
Some(amount)
}
#[must_use]
pub fn get_attributions(&self) -> Vec<PenaltyAttribution> {
self.applied.values().cloned().collect()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_grade_from_score() {
assert_eq!(Grade::from_score(95.0), Grade::APLus);
assert_eq!(Grade::from_score(90.0), Grade::A);
assert_eq!(Grade::from_score(85.0), Grade::AMinus);
assert_eq!(Grade::from_score(80.0), Grade::BPlus);
assert_eq!(Grade::from_score(75.0), Grade::B);
assert_eq!(Grade::from_score(70.0), Grade::BMinus);
assert_eq!(Grade::from_score(65.0), Grade::CPlus);
assert_eq!(Grade::from_score(60.0), Grade::C);
assert_eq!(Grade::from_score(55.0), Grade::CMinus);
assert_eq!(Grade::from_score(50.0), Grade::D);
assert_eq!(Grade::from_score(45.0), Grade::F);
}
#[test]
fn test_tdg_score_calculate_total() {
let mut score = TdgScore::default();
score.structural_complexity = 20.0;
score.semantic_complexity = 18.0;
score.duplication_ratio = 19.0;
score.coupling_score = 14.0;
score.doc_coverage = 9.0;
score.consistency_score = 8.0;
score.entropy_score = 12.0;
score.calculate_total();
assert_eq!(score.total, 100.0); assert_eq!(score.grade, Grade::APLus); }
#[test]
fn test_penalty_tracker() {
let mut tracker = PenaltyTracker::new();
let penalty1 = tracker.apply(
"issue1".to_string(),
MetricCategory::StructuralComplexity,
3.5,
"High cyclomatic complexity".to_string(),
);
assert_eq!(penalty1, Some(3.5));
let penalty2 = tracker.apply(
"issue1".to_string(),
MetricCategory::StructuralComplexity,
3.5,
"High cyclomatic complexity".to_string(),
);
assert_eq!(penalty2, None);
let attributions = tracker.get_attributions();
assert_eq!(attributions.len(), 1);
assert_eq!(attributions[0].amount, 3.5);
}
}
#[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);
}
}
}