Skip to main content

batuta/stack/quality/
summary.rs

1//! Quality summary statistics
2//!
3//! Contains `QualitySummary` - summary statistics for stack quality.
4
5use serde::{Deserialize, Serialize};
6
7use super::component::ComponentQuality;
8use super::types::QualityGrade;
9
10// ============================================================================
11// Quality Summary
12// ============================================================================
13
14/// Summary statistics for stack quality
15#[derive(Debug, Clone, Serialize, Deserialize)]
16pub struct QualitySummary {
17    /// Total components checked
18    pub total_components: usize,
19    /// Components with A+ grade
20    pub a_plus_count: usize,
21    /// Components with A grade
22    pub a_count: usize,
23    /// Components with A- grade
24    pub a_minus_count: usize,
25    /// Components below A- threshold
26    pub below_threshold_count: usize,
27    /// Components missing hero image
28    pub missing_hero_count: usize,
29    /// Average rust score
30    pub avg_rust_score: f64,
31    /// Average repo score
32    pub avg_repo_score: f64,
33    /// Average readme score
34    pub avg_readme_score: f64,
35}
36
37impl QualitySummary {
38    /// Calculate summary from component list
39    pub fn from_components(components: &[ComponentQuality]) -> Self {
40        let total = components.len();
41        if total == 0 {
42            return Self {
43                total_components: 0,
44                a_plus_count: 0,
45                a_count: 0,
46                a_minus_count: 0,
47                below_threshold_count: 0,
48                missing_hero_count: 0,
49                avg_rust_score: 0.0,
50                avg_repo_score: 0.0,
51                avg_readme_score: 0.0,
52            };
53        }
54
55        let a_plus_count = components.iter().filter(|c| c.grade == QualityGrade::APlus).count();
56        let a_count = components.iter().filter(|c| c.grade == QualityGrade::A).count();
57        let a_minus_count = components.iter().filter(|c| c.grade == QualityGrade::AMinus).count();
58        let below_threshold_count =
59            components.iter().filter(|c| !c.grade.is_release_ready()).count();
60        let missing_hero_count = components.iter().filter(|c| !c.hero_image.valid).count();
61
62        let avg_rust_score =
63            components.iter().map(|c| c.rust_score.value as f64).sum::<f64>() / total as f64;
64        let avg_repo_score =
65            components.iter().map(|c| c.repo_score.value as f64).sum::<f64>() / total as f64;
66        let avg_readme_score =
67            components.iter().map(|c| c.readme_score.value as f64).sum::<f64>() / total as f64;
68
69        Self {
70            total_components: total,
71            a_plus_count,
72            a_count,
73            a_minus_count,
74            below_threshold_count,
75            missing_hero_count,
76            avg_rust_score,
77            avg_repo_score,
78            avg_readme_score,
79        }
80    }
81}