use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
pub enum QualityGrade {
APlus,
A,
AMinus,
BPlus,
B,
C,
D,
F,
}
impl QualityGrade {
pub fn from_rust_project_score(score: u32) -> Self {
match score {
105..=114 => Self::APlus,
95..=104 => Self::A,
85..=94 => Self::AMinus,
80..=84 => Self::BPlus,
70..=79 => Self::B,
60..=69 => Self::C,
50..=59 => Self::D,
_ => Self::F,
}
}
pub fn from_repo_score(score: u32) -> Self {
match score {
95..=110 => Self::APlus,
90..=94 => Self::A,
85..=89 => Self::AMinus,
80..=84 => Self::BPlus,
70..=79 => Self::B,
60..=69 => Self::C,
50..=59 => Self::D,
_ => Self::F,
}
}
pub fn from_readme_score(score: u32) -> Self {
match score {
18..=20 => Self::APlus,
16..=17 => Self::A,
14..=15 => Self::AMinus,
12..=13 => Self::BPlus,
10..=11 => Self::B,
8..=9 => Self::C,
6..=7 => Self::D,
_ => Self::F,
}
}
pub fn from_sqi(sqi: f64) -> Self {
match sqi as u32 {
95..=100 => Self::APlus,
90..=94 => Self::A,
85..=89 => Self::AMinus,
80..=84 => Self::BPlus,
70..=79 => Self::B,
60..=69 => Self::C,
50..=59 => Self::D,
_ => Self::F,
}
}
pub fn is_release_ready(&self) -> bool {
matches!(self, Self::APlus | Self::A | Self::AMinus)
}
pub fn is_a_plus(&self) -> bool {
matches!(self, Self::APlus)
}
pub fn symbol(&self) -> &'static str {
match self {
Self::APlus => "A+",
Self::A => "A",
Self::AMinus => "A-",
Self::BPlus => "B+",
Self::B => "B",
Self::C => "C",
Self::D => "D",
Self::F => "F",
}
}
pub fn icon(&self) -> &'static str {
match self {
Self::APlus => "✅",
Self::A | Self::AMinus => "⚠️",
_ => "❌",
}
}
}
impl std::fmt::Display for QualityGrade {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.symbol())
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Score {
pub value: u32,
pub max: u32,
pub grade: QualityGrade,
}
impl Score {
pub fn new(value: u32, max: u32, grade: QualityGrade) -> Self {
Self { value, max, grade }
}
pub fn percentage(&self) -> f64 {
if self.max == 0 {
0.0
} else {
(self.value as f64 / self.max as f64) * 100.0
}
}
pub fn normalized(&self) -> f64 {
self.percentage()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum IssueSeverity {
Error,
Warning,
Info,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct QualityIssue {
pub issue_type: String,
pub message: String,
pub severity: IssueSeverity,
pub recommendation: Option<String>,
}
impl QualityIssue {
pub fn new(
issue_type: impl Into<String>,
message: impl Into<String>,
severity: IssueSeverity,
) -> Self {
Self {
issue_type: issue_type.into(),
message: message.into(),
severity,
recommendation: None,
}
}
pub fn with_recommendation(mut self, rec: impl Into<String>) -> Self {
self.recommendation = Some(rec.into());
self
}
pub fn score_below_threshold(metric: &str, score: u32, threshold: u32) -> Self {
Self::new(
format!("{}_below_threshold", metric),
format!("{} score {} below A- threshold ({})", metric, score, threshold),
IssueSeverity::Error,
)
.with_recommendation(format!("Improve {} to at least {}", metric, threshold))
}
pub fn missing_hero_image() -> Self {
Self::new("missing_hero_image", "No hero image found", IssueSeverity::Error)
.with_recommendation("Add hero.png to docs/ or include image at top of README.md")
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum StackLayer {
Compute,
Ml,
Training,
Transpilers,
Orchestration,
Quality,
DataMlops,
Presentation,
}
impl StackLayer {
pub fn from_component(name: &str) -> Self {
match name {
"trueno" | "trueno-viz" | "trueno-db" | "trueno-graph" | "trueno-rag"
| "trueno-zram" => Self::Compute,
"aprender" | "aprender-shell" | "aprender-tsp" => Self::Ml,
"entrenar" | "realizar" | "whisper-apr" => Self::Training,
"depyler" | "decy" | "ruchy" => Self::Transpilers,
"batuta" | "repartir" | "pepita" | "pforge" | "forjar" => Self::Orchestration,
"certeza" | "renacer" | "pmat" | "provable-contracts" | "tiny-model-ground-truth" => {
Self::Quality
}
"alimentar" | "pacha" => Self::DataMlops,
"presentar"
| "sovereign-ai-stack-book"
| "apr-cookbook"
| "alm-cookbook"
| "pres-cookbook" => Self::Presentation,
_ => Self::Orchestration, }
}
pub fn display_name(&self) -> &'static str {
match self {
Self::Compute => "COMPUTE PRIMITIVES",
Self::Ml => "ML ALGORITHMS",
Self::Training => "TRAINING & INFERENCE",
Self::Transpilers => "TRANSPILERS",
Self::Orchestration => "ORCHESTRATION",
Self::Quality => "QUALITY",
Self::DataMlops => "DATA & MLOPS",
Self::Presentation => "PRESENTATION",
}
}
}