use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum Gate {
Gate4 = 4,
Gate7 = 7,
Gate8 = 8,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum GateStatus {
Pass,
Fail,
Skipped,
NotApplicable,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GateResult {
pub gate: u8,
pub status: GateStatus,
pub checks: Vec<CheckResult>,
#[serde(skip_serializing_if = "Option::is_none")]
pub message: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CheckResult {
pub description: String,
pub status: GateStatus,
#[serde(skip_serializing_if = "Option::is_none")]
pub detail: Option<String>,
}
impl CheckResult {
pub fn pass(description: impl Into<String>) -> Self {
Self {
description: description.into(),
status: GateStatus::Pass,
detail: None,
}
}
pub fn fail(description: impl Into<String>, detail: impl Into<String>) -> Self {
Self {
description: description.into(),
status: GateStatus::Fail,
detail: Some(detail.into()),
}
}
pub fn skip(description: impl Into<String>) -> Self {
Self {
description: description.into(),
status: GateStatus::Skipped,
detail: None,
}
}
}
#[derive(Debug, Serialize, Deserialize)]
pub struct ValidationReport {
pub pack_name: String,
pub pack_version: String,
pub gates: Vec<GateResult>,
pub overall: GateStatus,
pub conformance: ConformanceLevel,
pub reviewed: bool,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum ConformanceLevel {
DkpConformant,
DkpReviewed,
NonConformant,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn check_result_pass_sets_pass_status_and_no_detail() {
let c = CheckResult::pass("thing checked");
assert_eq!(c.status, GateStatus::Pass);
assert_eq!(c.description, "thing checked");
assert!(c.detail.is_none());
}
#[test]
fn check_result_fail_sets_fail_status_and_detail() {
let c = CheckResult::fail("thing checked", "why it failed");
assert_eq!(c.status, GateStatus::Fail);
assert_eq!(c.detail.as_deref(), Some("why it failed"));
}
#[test]
fn check_result_skip_sets_skipped_status_and_no_detail() {
let c = CheckResult::skip("thing checked");
assert_eq!(c.status, GateStatus::Skipped);
assert!(c.detail.is_none());
}
#[test]
fn gate_status_serde_round_trip() {
for status in [
GateStatus::Pass,
GateStatus::Fail,
GateStatus::Skipped,
GateStatus::NotApplicable,
] {
let json = serde_json::to_string(&status).unwrap();
let back: GateStatus = serde_json::from_str(&json).unwrap();
assert_eq!(status, back);
}
}
#[test]
fn conformance_level_serde_round_trip() {
for level in [
ConformanceLevel::DkpConformant,
ConformanceLevel::DkpReviewed,
ConformanceLevel::NonConformant,
] {
let json = serde_json::to_string(&level).unwrap();
let back: ConformanceLevel = serde_json::from_str(&json).unwrap();
assert_eq!(level, back);
}
}
#[test]
fn gate_result_serde_round_trip_omits_none_message() {
let result = GateResult {
gate: 4,
status: GateStatus::Pass,
checks: vec![CheckResult::pass("ok")],
message: None,
};
let json = serde_json::to_string(&result).unwrap();
assert!(!json.contains("\"message\""));
let back: GateResult = serde_json::from_str(&json).unwrap();
assert_eq!(back.gate, 4);
assert_eq!(back.status, GateStatus::Pass);
assert_eq!(back.checks.len(), 1);
}
}