#![allow(dead_code, unused_imports, unused_variables)]
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum QaProfile {
#[default]
Standard,
Strict,
Minimal,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
pub enum QualityGrade {
S,
A,
B,
C,
D,
F,
}
impl QualityGrade {
pub fn from_score(score: f64) -> Self {
if score >= 95.0 {
Self::S
} else if score >= 90.0 {
Self::A
} else if score >= 80.0 {
Self::B
} else if score >= 70.0 {
Self::C
} else if score >= 60.0 {
Self::D
} else {
Self::F
}
}
}
impl std::fmt::Display for QualityGrade {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::S => write!(f, "S"),
Self::A => write!(f, "A"),
Self::B => write!(f, "B"),
Self::C => write!(f, "C"),
Self::D => write!(f, "D"),
Self::F => write!(f, "F"),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct QaWeights {
pub syntax: f64,
pub format: f64,
pub lint: f64,
pub type_check: f64,
pub test: f64,
pub security: f64,
}
impl QaWeights {
pub fn standard() -> Self {
Self {
syntax: 10.0,
format: 5.0,
lint: 15.0,
type_check: 10.0,
test: 30.0,
security: 10.0,
}
}
pub fn strict() -> Self {
Self {
syntax: 10.0,
format: 10.0,
lint: 20.0,
type_check: 15.0,
test: 30.0,
security: 15.0,
}
}
pub fn minimal() -> Self {
Self {
syntax: 40.0,
format: 0.0,
lint: 0.0,
type_check: 40.0,
test: 0.0,
security: 0.0,
}
}
pub fn total(&self) -> f64 {
self.syntax + self.format + self.lint + self.type_check + self.test + self.security
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct QaConfig {
#[serde(default)]
pub profile: QaProfile,
#[serde(default = "default_auto_fix_iterations")]
pub auto_fix_iterations: u32,
#[serde(default = "default_test_retry_iterations")]
pub test_retry_iterations: u32,
}
impl QaConfig {}
impl QaWeights {}
fn default_auto_fix_iterations() -> u32 {
3
}
fn default_test_retry_iterations() -> u32 {
2
}
impl Default for QaConfig {
fn default() -> Self {
Self {
profile: QaProfile::Standard,
auto_fix_iterations: default_auto_fix_iterations(),
test_retry_iterations: default_test_retry_iterations(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct QaReport {
pub language: String,
pub profile: QaProfile,
pub stages: Vec<QaStageResult>,
pub score: f64,
pub grade: QualityGrade,
pub total_duration_ms: u64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct QaStageResult {
pub stage: QaStage,
pub passed: bool,
pub duration_ms: u64,
pub output: String,
pub error_count: usize,
pub warning_count: usize,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum QaStage {
Syntax,
Format,
Lint,
TypeCheck,
Test,
Security,
}
impl std::fmt::Display for QaStage {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Syntax => write!(f, "Syntax"),
Self::Format => write!(f, "Format"),
Self::Lint => write!(f, "Lint"),
Self::TypeCheck => write!(f, "TypeCheck"),
Self::Test => write!(f, "Test"),
Self::Security => write!(f, "Security"),
}
}
}
pub fn compute_score(stages: &[QaStageResult], weights: &QaWeights) -> f64 {
let total_weight = weights.total();
if total_weight <= 0.0 {
return 100.0;
}
let mut earned = 0.0;
for stage in stages {
let weight = match stage.stage {
QaStage::Syntax => weights.syntax,
QaStage::Format => weights.format,
QaStage::Lint => weights.lint,
QaStage::TypeCheck => weights.type_check,
QaStage::Test => weights.test,
QaStage::Security => weights.security,
};
if stage.passed {
earned += weight;
} else if stage.warning_count > 0 && stage.error_count == 0 {
earned += weight * 0.5;
}
}
(earned / total_weight * 100.0).min(100.0)
}
#[cfg(test)]
#[path = "../../tests/unit/testing/qa_profiles/qa_profiles_test.rs"]
mod tests;