Skip to main content

chio_web3/
qualification.rs

1use std::collections::HashSet;
2
3use serde::{Deserialize, Serialize};
4
5use crate::error::Web3ContractError;
6use crate::settlement::Web3SettlementLifecycleState;
7use crate::validation::{ensure_non_empty, ensure_unique_strings};
8
9pub const CHIO_WEB3_QUALIFICATION_MATRIX_SCHEMA: &str = "chio.web3-qualification-matrix.v1";
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
12#[serde(rename_all = "snake_case")]
13pub enum Web3QualificationOutcome {
14    Pass,
15    FailClosed,
16}
17
18#[derive(Debug, Clone, Serialize, Deserialize)]
19#[serde(deny_unknown_fields)]
20pub struct Web3QualificationCase {
21    pub id: String,
22    pub name: String,
23    pub requirement_ids: Vec<String>,
24    pub lifecycle_state: Web3SettlementLifecycleState,
25    pub expected_outcome: Web3QualificationOutcome,
26    pub observed_outcome: Web3QualificationOutcome,
27    pub notes: String,
28}
29
30#[derive(Debug, Clone, Serialize, Deserialize)]
31#[serde(deny_unknown_fields)]
32pub struct Web3QualificationMatrix {
33    pub schema: String,
34    pub trust_profile_id: String,
35    pub contract_package_id: String,
36    pub cases: Vec<Web3QualificationCase>,
37}
38pub fn validate_web3_qualification_matrix(
39    matrix: &Web3QualificationMatrix,
40) -> Result<(), Web3ContractError> {
41    if matrix.schema != CHIO_WEB3_QUALIFICATION_MATRIX_SCHEMA {
42        return Err(Web3ContractError::UnsupportedSchema(matrix.schema.clone()));
43    }
44    ensure_non_empty(
45        &matrix.trust_profile_id,
46        "web3_qualification_matrix.trust_profile_id",
47    )?;
48    ensure_non_empty(
49        &matrix.contract_package_id,
50        "web3_qualification_matrix.contract_package_id",
51    )?;
52    if matrix.cases.is_empty() {
53        return Err(Web3ContractError::MissingField(
54            "web3_qualification_matrix.cases",
55        ));
56    }
57    let mut case_ids = HashSet::new();
58    for case in &matrix.cases {
59        ensure_non_empty(&case.id, "web3_qualification_matrix.case.id")?;
60        ensure_non_empty(&case.name, "web3_qualification_matrix.case.name")?;
61        ensure_non_empty(&case.notes, "web3_qualification_matrix.case.notes")?;
62        if !case_ids.insert(case.id.as_str()) {
63            return Err(Web3ContractError::DuplicateValue(case.id.clone()));
64        }
65        if case.requirement_ids.is_empty() {
66            return Err(Web3ContractError::InvalidQualificationCase(format!(
67                "case {} must cite at least one requirement id",
68                case.id
69            )));
70        }
71        ensure_unique_strings(
72            &case.requirement_ids,
73            "web3_qualification_matrix.case.requirement_ids",
74        )?;
75    }
76    Ok(())
77}