batuta/stack/quality/
component.rs1use serde::{Deserialize, Serialize};
6use std::path::PathBuf;
7
8use super::types::{QualityGrade, QualityIssue, Score, StackLayer};
9use crate::stack::hero_image::HeroImageResult;
10
11#[derive(Debug, Clone, Serialize, Deserialize)]
17pub struct ComponentQuality {
18 pub name: String,
20 pub layer: StackLayer,
22 pub path: PathBuf,
24 pub rust_score: Score,
26 pub repo_score: Score,
28 pub readme_score: Score,
30 pub hero_image: HeroImageResult,
32 pub sqi: f64,
34 pub grade: QualityGrade,
36 pub release_ready: bool,
38 pub issues: Vec<QualityIssue>,
40}
41
42impl ComponentQuality {
43 pub fn new(
45 name: impl Into<String>,
46 path: PathBuf,
47 rust_score: Score,
48 repo_score: Score,
49 readme_score: Score,
50 hero_image: HeroImageResult,
51 ) -> Self {
52 let name = name.into();
53 let layer = StackLayer::from_component(&name);
54 let sqi = Self::calculate_sqi(&rust_score, &repo_score, &readme_score, &hero_image);
55 let grade = QualityGrade::from_sqi(sqi);
56 let issues = Self::collect_issues(&rust_score, &repo_score, &readme_score, &hero_image);
57 let release_ready = grade.is_release_ready() && hero_image.valid;
58
59 Self {
60 name,
61 layer,
62 path,
63 rust_score,
64 repo_score,
65 readme_score,
66 hero_image,
67 sqi,
68 grade,
69 release_ready,
70 issues,
71 }
72 }
73
74 pub fn calculate_sqi(
77 rust: &Score,
78 repo: &Score,
79 readme: &Score,
80 hero: &HeroImageResult,
81 ) -> f64 {
82 let rust_normalized = rust.normalized();
83 let repo_normalized = repo.normalized();
84 let readme_normalized = readme.normalized();
85 let hero_normalized = if hero.valid { 100.0 } else { 0.0 };
86
87 (0.40 * rust_normalized)
88 + (0.30 * repo_normalized)
89 + (0.20 * readme_normalized)
90 + (0.10 * hero_normalized)
91 }
92
93 fn collect_issues(
95 rust: &Score,
96 repo: &Score,
97 readme: &Score,
98 hero: &HeroImageResult,
99 ) -> Vec<QualityIssue> {
100 let mut issues = Vec::new();
101
102 if rust.value < 85 {
104 issues.push(QualityIssue::score_below_threshold("rust_project", rust.value, 85));
105 }
106
107 if repo.value < 85 {
109 issues.push(QualityIssue::score_below_threshold("repo", repo.value, 85));
110 }
111
112 if readme.value < 14 {
114 issues.push(QualityIssue::score_below_threshold("readme", readme.value, 14));
115 }
116
117 if !hero.valid {
119 issues.push(QualityIssue::missing_hero_image());
120 }
121
122 issues
123 }
124}