use crate::core::refined::{MultiplierFactor, WeightFactor};
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ScoringWeights {
#[serde(default = "default_coverage_weight")]
pub coverage: f64,
#[serde(default = "default_complexity_weight")]
pub complexity: f64,
#[serde(default = "default_semantic_weight")]
pub semantic: f64,
#[serde(default = "default_dependency_weight")]
pub dependency: f64,
#[serde(default = "default_security_weight")]
pub security: f64,
#[serde(default = "default_organization_weight")]
pub organization: f64,
}
impl Default for ScoringWeights {
fn default() -> Self {
Self {
coverage: default_coverage_weight(),
complexity: default_complexity_weight(),
semantic: default_semantic_weight(),
dependency: default_dependency_weight(),
security: default_security_weight(),
organization: default_organization_weight(),
}
}
}
impl ScoringWeights {
pub fn is_valid_weight(weight: f64) -> bool {
(0.0..=1.0).contains(&weight)
}
pub fn validate_weight(weight: f64, name: &str) -> Result<(), String> {
if Self::is_valid_weight(weight) {
Ok(())
} else {
Err(format!("{} weight must be between 0.0 and 1.0", name))
}
}
pub fn validate_active_weights_sum(
coverage: f64,
complexity: f64,
dependency: f64,
) -> Result<(), String> {
let sum = coverage + complexity + dependency;
if (sum - 1.0).abs() > 0.001 {
Err(format!(
"Active scoring weights (coverage, complexity, dependency) must sum to 1.0, but sum to {:.3}",
sum
))
} else {
Ok(())
}
}
pub fn collect_weight_validations(&self) -> Vec<Result<(), String>> {
vec![
Self::validate_weight(self.coverage, "Coverage"),
Self::validate_weight(self.complexity, "Complexity"),
Self::validate_weight(self.semantic, "Semantic"),
Self::validate_weight(self.dependency, "Dependency"),
Self::validate_weight(self.security, "Security"),
Self::validate_weight(self.organization, "Organization"),
]
}
pub fn validate(&self) -> Result<(), String> {
Self::validate_active_weights_sum(self.coverage, self.complexity, self.dependency)?;
for validation in self.collect_weight_validations() {
validation?;
}
Ok(())
}
pub fn normalize(&mut self) {
let sum = self.coverage + self.complexity + self.dependency;
if sum > 0.0 && (sum - 1.0).abs() > 0.001 {
self.coverage /= sum;
self.complexity /= sum;
self.dependency /= sum;
self.semantic = 0.0;
self.security = 0.0;
self.organization = 0.0;
}
}
pub fn coverage_refined(&self) -> Option<WeightFactor> {
WeightFactor::new(self.coverage).ok()
}
pub fn complexity_refined(&self) -> Option<WeightFactor> {
WeightFactor::new(self.complexity).ok()
}
pub fn dependency_refined(&self) -> Option<WeightFactor> {
WeightFactor::new(self.dependency).ok()
}
pub fn active_weights_refined(&self) -> Option<(WeightFactor, WeightFactor, WeightFactor)> {
Some((
self.coverage_refined()?,
self.complexity_refined()?,
self.dependency_refined()?,
))
}
pub fn validate_refined(&self) -> Vec<String> {
let mut errors = Vec::new();
if self.coverage_refined().is_none() {
errors.push(format!(
"coverage weight {} is invalid (must be 0.0-1.0)",
self.coverage
));
}
if self.complexity_refined().is_none() {
errors.push(format!(
"complexity weight {} is invalid (must be 0.0-1.0)",
self.complexity
));
}
if self.dependency_refined().is_none() {
errors.push(format!(
"dependency weight {} is invalid (must be 0.0-1.0)",
self.dependency
));
}
errors
}
}
pub fn default_coverage_weight() -> f64 {
0.50 }
pub fn default_complexity_weight() -> f64 {
0.35 }
pub fn default_semantic_weight() -> f64 {
0.00 }
pub fn default_dependency_weight() -> f64 {
0.15 }
pub fn default_security_weight() -> f64 {
0.00 }
pub fn default_organization_weight() -> f64 {
0.00 }
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RoleMultipliers {
#[serde(default = "default_pure_logic_multiplier")]
pub pure_logic: f64,
#[serde(default = "default_orchestrator_multiplier")]
pub orchestrator: f64,
#[serde(default = "default_io_wrapper_multiplier")]
pub io_wrapper: f64,
#[serde(default = "default_entry_point_multiplier")]
pub entry_point: f64,
#[serde(default = "default_pattern_match_multiplier")]
pub pattern_match: f64,
#[serde(default = "default_debug_multiplier")]
pub debug: f64,
#[serde(default = "default_unknown_multiplier")]
pub unknown: f64,
}
impl Default for RoleMultipliers {
fn default() -> Self {
Self {
pure_logic: default_pure_logic_multiplier(),
orchestrator: default_orchestrator_multiplier(),
io_wrapper: default_io_wrapper_multiplier(),
entry_point: default_entry_point_multiplier(),
pattern_match: default_pattern_match_multiplier(),
debug: default_debug_multiplier(),
unknown: default_unknown_multiplier(),
}
}
}
impl RoleMultipliers {
pub fn pure_logic_refined(&self) -> Option<MultiplierFactor> {
MultiplierFactor::new(self.pure_logic).ok()
}
pub fn orchestrator_refined(&self) -> Option<MultiplierFactor> {
MultiplierFactor::new(self.orchestrator).ok()
}
pub fn io_wrapper_refined(&self) -> Option<MultiplierFactor> {
MultiplierFactor::new(self.io_wrapper).ok()
}
pub fn entry_point_refined(&self) -> Option<MultiplierFactor> {
MultiplierFactor::new(self.entry_point).ok()
}
pub fn debug_refined(&self) -> Option<MultiplierFactor> {
MultiplierFactor::new(self.debug).ok()
}
pub fn validate_refined(&self) -> Vec<String> {
let mut errors = Vec::new();
let multipliers = [
("pure_logic", self.pure_logic),
("orchestrator", self.orchestrator),
("io_wrapper", self.io_wrapper),
("entry_point", self.entry_point),
("pattern_match", self.pattern_match),
("debug", self.debug),
("unknown", self.unknown),
];
for (name, value) in multipliers {
if MultiplierFactor::new(value).is_err() {
errors.push(format!(
"{} multiplier {} is invalid (must be > 0.0)",
name, value
));
}
}
errors
}
}
pub fn default_pure_logic_multiplier() -> f64 {
0.7 }
pub fn default_orchestrator_multiplier() -> f64 {
0.8 }
pub fn default_io_wrapper_multiplier() -> f64 {
0.7 }
pub fn default_entry_point_multiplier() -> f64 {
0.9 }
pub fn default_pattern_match_multiplier() -> f64 {
0.6 }
pub fn default_debug_multiplier() -> f64 {
0.3 }
pub fn default_unknown_multiplier() -> f64 {
1.0 }
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ComplexityWeightsConfig {
#[serde(default = "default_cyclomatic_weight")]
pub cyclomatic: f64,
#[serde(default = "default_cognitive_weight")]
pub cognitive: f64,
#[serde(default = "default_max_cyclomatic")]
pub max_cyclomatic: f64,
#[serde(default = "default_max_cognitive")]
pub max_cognitive: f64,
}
impl Default for ComplexityWeightsConfig {
fn default() -> Self {
Self {
cyclomatic: default_cyclomatic_weight(),
cognitive: default_cognitive_weight(),
max_cyclomatic: default_max_cyclomatic(),
max_cognitive: default_max_cognitive(),
}
}
}
pub fn default_cyclomatic_weight() -> f64 {
0.3 }
pub fn default_cognitive_weight() -> f64 {
0.7 }
pub fn default_max_cyclomatic() -> f64 {
50.0 }
pub fn default_max_cognitive() -> f64 {
100.0 }
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RoleCoverageWeights {
#[serde(default = "default_entry_point_coverage_weight")]
pub entry_point: f64,
#[serde(default = "default_orchestrator_coverage_weight")]
pub orchestrator: f64,
#[serde(default = "default_pure_logic_coverage_weight")]
pub pure_logic: f64,
#[serde(default = "default_io_wrapper_coverage_weight")]
pub io_wrapper: f64,
#[serde(default = "default_pattern_match_coverage_weight")]
pub pattern_match: f64,
#[serde(default = "default_debug_coverage_weight")]
pub debug: f64,
#[serde(default = "default_unknown_coverage_weight")]
pub unknown: f64,
}
impl Default for RoleCoverageWeights {
fn default() -> Self {
Self {
entry_point: default_entry_point_coverage_weight(),
orchestrator: default_orchestrator_coverage_weight(),
pure_logic: default_pure_logic_coverage_weight(),
io_wrapper: default_io_wrapper_coverage_weight(),
pattern_match: default_pattern_match_coverage_weight(),
debug: default_debug_coverage_weight(),
unknown: default_unknown_coverage_weight(),
}
}
}
pub fn default_entry_point_coverage_weight() -> f64 {
0.6 }
pub fn default_orchestrator_coverage_weight() -> f64 {
0.8 }
pub fn default_pure_logic_coverage_weight() -> f64 {
1.0 }
pub fn default_io_wrapper_coverage_weight() -> f64 {
0.5 }
pub fn default_pattern_match_coverage_weight() -> f64 {
1.0 }
pub fn default_debug_coverage_weight() -> f64 {
0.3 }
pub fn default_unknown_coverage_weight() -> f64 {
1.0 }
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RoleMultiplierConfig {
#[serde(default = "default_role_clamp_min")]
pub clamp_min: f64,
#[serde(default = "default_role_clamp_max")]
pub clamp_max: f64,
#[serde(default = "default_enable_role_clamping")]
pub enable_clamping: bool,
}
impl Default for RoleMultiplierConfig {
fn default() -> Self {
Self {
clamp_min: default_role_clamp_min(),
clamp_max: default_role_clamp_max(),
enable_clamping: default_enable_role_clamping(),
}
}
}
pub fn default_role_clamp_min() -> f64 {
0.3 }
pub fn default_role_clamp_max() -> f64 {
1.8 }
pub fn default_enable_role_clamping() -> bool {
true }
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RebalancedScoringConfig {
#[serde(default)]
pub preset: Option<String>,
#[serde(default)]
pub complexity_weight: Option<f64>,
#[serde(default)]
pub coverage_weight: Option<f64>,
#[serde(default)]
pub structural_weight: Option<f64>,
#[serde(default)]
pub size_weight: Option<f64>,
#[serde(default)]
pub smell_weight: Option<f64>,
}
impl RebalancedScoringConfig {
pub fn to_weights(&self) -> crate::priority::scoring::ScoreWeights {
use crate::priority::scoring::ScoreWeights;
let mut weights = self
.preset
.as_ref()
.and_then(|p| ScoreWeights::from_preset(p))
.unwrap_or_default();
if let Some(w) = self.complexity_weight {
weights.complexity_weight = w;
}
if let Some(w) = self.coverage_weight {
weights.coverage_weight = w;
}
if let Some(w) = self.structural_weight {
weights.structural_weight = w;
}
if let Some(w) = self.size_weight {
weights.size_weight = w;
}
if let Some(w) = self.smell_weight {
weights.smell_weight = w;
}
weights
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct NormalizationConfig {
#[serde(default = "default_linear_threshold")]
pub linear_threshold: f64,
#[serde(default = "default_logarithmic_threshold")]
pub logarithmic_threshold: f64,
#[serde(default = "default_sqrt_multiplier")]
pub sqrt_multiplier: f64,
#[serde(default = "default_log_multiplier")]
pub log_multiplier: f64,
#[serde(default = "default_show_raw_scores")]
pub show_raw_scores: bool,
}
impl Default for NormalizationConfig {
fn default() -> Self {
Self {
linear_threshold: default_linear_threshold(),
logarithmic_threshold: default_logarithmic_threshold(),
sqrt_multiplier: default_sqrt_multiplier(),
log_multiplier: default_log_multiplier(),
show_raw_scores: default_show_raw_scores(),
}
}
}
pub fn default_linear_threshold() -> f64 {
10.0
}
pub fn default_logarithmic_threshold() -> f64 {
100.0
}
pub fn default_sqrt_multiplier() -> f64 {
3.33
}
pub fn default_log_multiplier() -> f64 {
10.0
}
pub fn default_show_raw_scores() -> bool {
true
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ContextMultipliers {
#[serde(default = "default_examples_multiplier")]
pub examples: f64,
#[serde(default = "default_tests_multiplier")]
pub tests: f64,
#[serde(default = "default_benchmarks_multiplier")]
pub benchmarks: f64,
#[serde(default = "default_build_scripts_multiplier")]
pub build_scripts: f64,
#[serde(default = "default_documentation_multiplier")]
pub documentation: f64,
#[serde(default = "default_enable_context_dampening")]
pub enable_context_dampening: bool,
}
impl Default for ContextMultipliers {
fn default() -> Self {
Self {
examples: default_examples_multiplier(),
tests: default_tests_multiplier(),
benchmarks: default_benchmarks_multiplier(),
build_scripts: default_build_scripts_multiplier(),
documentation: default_documentation_multiplier(),
enable_context_dampening: default_enable_context_dampening(),
}
}
}
pub fn default_examples_multiplier() -> f64 {
0.1 }
pub fn default_tests_multiplier() -> f64 {
0.2 }
pub fn default_benchmarks_multiplier() -> f64 {
0.3 }
pub fn default_build_scripts_multiplier() -> f64 {
0.3 }
pub fn default_documentation_multiplier() -> f64 {
0.1 }
pub fn default_enable_context_dampening() -> bool {
false }
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DataFlowScoringConfig {
#[serde(default = "default_data_flow_enabled")]
pub enabled: bool,
#[serde(default = "default_purity_weight")]
pub purity_weight: f64,
#[serde(default = "default_refactorability_weight")]
pub refactorability_weight: f64,
#[serde(default = "default_pattern_weight")]
pub pattern_weight: f64,
}
impl Default for DataFlowScoringConfig {
fn default() -> Self {
Self {
enabled: default_data_flow_enabled(),
purity_weight: default_purity_weight(),
refactorability_weight: default_refactorability_weight(),
pattern_weight: default_pattern_weight(),
}
}
}
pub fn default_data_flow_enabled() -> bool {
true
}
pub fn default_purity_weight() -> f64 {
0.4
}
pub fn default_refactorability_weight() -> f64 {
0.3
}
pub fn default_pattern_weight() -> f64 {
0.3
}