Skip to main content

crabscore_core/
scoring.rs

1//! Scoring logic for CrabScore
2
3use crate::{
4    metrics::{CostMetrics, EnergyMetrics, PerformanceMetrics, SafetyMetrics},
5    CrabScore, IndustryProfile,
6};
7
8/// Engine for calculating CrabScores
9pub struct ScoringEngine {
10    profile: IndustryProfile,
11}
12
13impl ScoringEngine {
14    /// Create a new scoring engine with the given industry profile
15    pub fn new(profile: IndustryProfile) -> Self {
16        Self { profile }
17    }
18
19    /// Calculate a CrabScore based on the provided metrics
20    pub fn calculate_score(
21        &self,
22        performance: &PerformanceMetrics,
23        energy: &EnergyMetrics,
24        cost: &CostMetrics,
25        safety: &SafetyMetrics,
26    ) -> CrabScore {
27        let weights = self.profile.weights();
28
29        let perf_score = self.score_performance(performance);
30        let energy_score = self.score_energy(energy);
31        let cost_score = self.score_cost(cost);
32        let bonuses = self.score_safety(safety);
33
34        let overall = (perf_score * weights.performance)
35            + (energy_score * weights.energy)
36            + (cost_score * weights.cost)
37            + bonuses;
38
39        let certification = if overall >= 85.0 {
40            crate::Certification::Certified
41        } else if overall >= 70.0 {
42            crate::Certification::Verified
43        } else {
44            crate::Certification::None
45        };
46
47        let metadata = crate::ScoreMetadata {
48            project_name: String::new(),
49            version: String::new(),
50            profile: self.profile,
51            measurements: crate::MeasurementSummary {
52                duration: std::time::Duration::from_secs(0),
53                iterations: 0,
54                environment: crate::Environment {
55                    os: String::new(),
56                    cpu: String::new(),
57                    memory_gb: 0.0,
58                    rust_version: String::new(),
59                },
60            },
61        };
62
63        CrabScore {
64            overall,
65            performance: perf_score,
66            energy: energy_score,
67            cost: cost_score,
68            bonuses,
69            certification,
70            timestamp: chrono::Utc::now(),
71            metadata,
72        }
73    }
74
75    // ---------------------------------------------------------------------
76    // Internal helpers
77    // ---------------------------------------------------------------------
78
79    fn clamp(v: f64) -> f64 {
80        v.clamp(0.0, 100.0)
81    }
82
83    fn score_performance(&self, m: &PerformanceMetrics) -> f64 {
84        // Simple heuristic combining latency (lower better) & throughput (higher better)
85        let latency_ms = m.latency.p95_ms.max(1.0); // avoid div-by-zero
86        let latency_score = (1.0 / (1.0 + latency_ms / 100.0)) * 100.0;
87
88        let tps = m.throughput.requests_per_second;
89        let throughput_score = (tps / (tps + 1000.0)) * 100.0;
90
91        let resource_score = 100.0 * m.resource_usage.cpu_efficiency.min(1.0);
92
93        Self::clamp((latency_score + throughput_score + resource_score) / 3.0)
94    }
95
96    fn score_energy(&self, m: &EnergyMetrics) -> f64 {
97        // Lower power and higher renewable percentage boost score
98        let watts = m.direct_consumption.average_watts.max(1.0);
99        let power_score = (1.0 / (1.0 + watts / 100.0)) * 100.0;
100        let renewable_score = m.carbon_efficiency.renewable_percentage * 100.0;
101        Self::clamp((power_score + renewable_score) / 2.0)
102    }
103
104    fn score_cost(&self, m: &CostMetrics) -> f64 {
105        let infra = m.infrastructure.cloud_compute_usd;
106        let infra_score = (1.0 / (1.0 + infra / 1000.0)) * 100.0;
107        let ops_score = (1.0 / (1.0 + m.operations.overhead_percentage)) * 100.0;
108        Self::clamp((infra_score + ops_score) / 2.0)
109    }
110
111    fn score_safety(&self, s: &SafetyMetrics) -> f64 {
112        let mut bonus = 0.0;
113        if s.unsafe_blocks == 0 {
114            bonus += 4.0;
115        }
116        if s.clippy_warnings == 0 {
117            bonus += 3.0;
118        }
119        if s.avg_cyclomatic <= 10.0 {
120            bonus += 3.0;
121        }
122        bonus // out of 10 max, added directly
123    }
124}