mod ai;
mod baseline;
mod dot;
pub(crate) mod findings_list;
mod github;
mod github_annotations;
mod html;
mod json;
mod json_srp;
mod json_structural;
mod json_tq;
mod json_types;
mod sarif;
mod suggestions;
mod text;
pub use ai::{print_ai, print_ai_json};
pub use baseline::{create_baseline, print_comparison};
pub use dot::print_dot;
pub use github::print_github;
pub use github_annotations::print_coupling_annotations;
pub use github_annotations::print_dry_annotations;
pub use github_annotations::print_srp_annotations;
pub use github_annotations::print_structural_annotations;
pub use github_annotations::print_tq_annotations;
pub use html::print_html;
pub use json::print_json;
pub use sarif::print_sarif;
pub use suggestions::print_suggestions;
pub use text::print_coupling_section;
pub use text::print_dry_section;
pub use text::print_files_only;
pub use text::print_srp_section;
pub(crate) use text::print_structural_section;
pub use text::print_summary_only;
pub(crate) use text::print_tq_section;
use crate::adapters::analyzers::dry::boilerplate::BoilerplateFind;
use crate::adapters::analyzers::dry::dead_code::DeadCodeWarning;
use crate::adapters::analyzers::dry::fragments::FragmentGroup;
use crate::adapters::analyzers::dry::functions::DuplicateGroup;
use crate::adapters::analyzers::dry::wildcards::WildcardImportWarning;
use crate::adapters::analyzers::iosp::{Classification, FunctionAnalysis};
pub struct AnalysisResult {
pub results: Vec<FunctionAnalysis>,
pub summary: Summary,
pub coupling: Option<crate::adapters::analyzers::coupling::CouplingAnalysis>,
pub duplicates: Vec<DuplicateGroup>,
pub dead_code: Vec<DeadCodeWarning>,
pub fragments: Vec<FragmentGroup>,
pub boilerplate: Vec<BoilerplateFind>,
pub wildcard_warnings: Vec<WildcardImportWarning>,
pub repeated_matches: Vec<crate::adapters::analyzers::dry::match_patterns::RepeatedMatchGroup>,
pub srp: Option<crate::adapters::analyzers::srp::SrpAnalysis>,
pub tq: Option<crate::adapters::analyzers::tq::TqAnalysis>,
pub structural: Option<crate::adapters::analyzers::structural::StructuralAnalysis>,
pub architecture_findings: Vec<crate::domain::Finding>,
pub orphan_suppressions: Vec<OrphanSuppressionWarning>,
}
#[derive(Debug, Clone)]
pub struct OrphanSuppressionWarning {
pub file: String,
pub line: usize,
pub dimensions: Vec<crate::domain::Dimension>,
pub reason: Option<String>,
}
#[derive(Debug, Default)]
pub struct Summary {
pub total: usize,
pub integrations: usize,
pub operations: usize,
pub violations: usize,
pub trivial: usize,
pub suppressed: usize,
pub iosp_score: f64,
pub complexity_warnings: usize,
pub magic_number_warnings: usize,
pub nesting_depth_warnings: usize,
pub function_length_warnings: usize,
pub unsafe_warnings: usize,
pub error_handling_warnings: usize,
pub coupling_warnings: usize,
pub coupling_cycles: usize,
pub duplicate_groups: usize,
pub dead_code_warnings: usize,
pub fragment_groups: usize,
pub boilerplate_warnings: usize,
pub srp_struct_warnings: usize,
pub srp_module_warnings: usize,
pub srp_param_warnings: usize,
pub wildcard_import_warnings: usize,
pub repeated_match_groups: usize,
pub sdp_violations: usize,
pub tq_no_assertion_warnings: usize,
pub tq_no_sut_warnings: usize,
pub tq_untested_warnings: usize,
pub tq_uncovered_warnings: usize,
pub tq_untested_logic_warnings: usize,
pub structural_srp_warnings: usize,
pub structural_coupling_warnings: usize,
pub architecture_warnings: usize,
pub quality_score: f64,
pub dimension_scores: [f64; 7],
pub all_suppressions: usize,
pub suppression_ratio_exceeded: bool,
pub orphan_suppressions: usize,
}
impl Summary {
pub fn from_results(results: &[FunctionAnalysis]) -> Self {
let mut s = Self {
total: results.len(),
..Default::default()
};
for r in results {
if r.suppressed {
s.suppressed += 1;
continue;
}
match &r.classification {
Classification::Integration => s.integrations += 1,
Classification::Operation => s.operations += 1,
Classification::Violation { .. } => s.violations += 1,
Classification::Trivial => s.trivial += 1,
}
}
let non_trivial = s.integrations + s.operations + s.violations;
s.iosp_score = if non_trivial > 0 {
(s.integrations + s.operations) as f64 / non_trivial as f64
} else {
1.0
};
s
}
pub fn compute_quality_score(&mut self, weights: &[f64; 7]) {
let n = self.total.max(1) as f64;
let complexity_count = self.complexity_warnings
+ self.magic_number_warnings
+ self.nesting_depth_warnings
+ self.function_length_warnings
+ self.unsafe_warnings
+ self.error_handling_warnings;
let tq_count = self.tq_no_assertion_warnings
+ self.tq_no_sut_warnings
+ self.tq_untested_warnings
+ self.tq_uncovered_warnings
+ self.tq_untested_logic_warnings;
self.dimension_scores = [
self.iosp_score,
1.0 - (complexity_count as f64 / n).min(1.0),
1.0 - ((self.duplicate_groups
+ self.fragment_groups
+ self.dead_code_warnings
+ self.boilerplate_warnings
+ self.wildcard_import_warnings
+ self.repeated_match_groups) as f64
/ n)
.min(1.0),
1.0 - ((self.srp_struct_warnings
+ self.srp_module_warnings
+ self.srp_param_warnings
+ self.structural_srp_warnings) as f64
/ n)
.min(1.0),
1.0 - ((self.coupling_warnings
+ self.coupling_cycles * 2
+ self.sdp_violations
+ self.structural_coupling_warnings) as f64
/ n)
.min(1.0),
1.0 - (tq_count as f64 / n).min(1.0),
1.0 - (self.architecture_warnings as f64 / n).min(1.0),
];
let active_dims = weights.iter().filter(|&&w| w > f64::EPSILON).count() as f64;
let weighted_avg: f64 = self
.dimension_scores
.iter()
.zip(weights.iter())
.map(|(s, w)| s * w)
.sum();
let scale = if active_dims > 0.0 { active_dims } else { 1.0 };
self.quality_score = (1.0 - scale * (1.0 - weighted_avg)).clamp(0.0, 1.0);
}
pub fn total_findings(&self) -> usize {
self.violations
+ self.complexity_warnings
+ self.magic_number_warnings
+ self.nesting_depth_warnings
+ self.function_length_warnings
+ self.unsafe_warnings
+ self.error_handling_warnings
+ self.duplicate_groups
+ self.fragment_groups
+ self.dead_code_warnings
+ self.boilerplate_warnings
+ self.srp_struct_warnings
+ self.srp_module_warnings
+ self.srp_param_warnings
+ self.wildcard_import_warnings
+ self.repeated_match_groups
+ self.coupling_warnings
+ self.coupling_cycles
+ self.sdp_violations
+ self.tq_no_assertion_warnings
+ self.tq_no_sut_warnings
+ self.tq_untested_warnings
+ self.tq_uncovered_warnings
+ self.tq_untested_logic_warnings
+ self.structural_srp_warnings
+ self.structural_coupling_warnings
+ self.architecture_warnings
+ self.orphan_suppressions
}
}
#[cfg(test)]
mod tests;