#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct ControlId(pub &'static str);
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum CertificationFramework {
Iso27001,
FedrampRev5,
CsaCcm,
PciDss,
Custom(&'static str),
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ControlMapping {
pub control: ControlId,
pub framework: CertificationFramework,
pub satisfied_by: Vec<&'static str>,
}
impl ControlMapping {
#[must_use]
pub fn is_grounded(&self) -> bool {
!self.satisfied_by.is_empty()
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct CertificationEnvelope {
pub framework: CertificationFramework,
pub mappings: Vec<ControlMapping>,
pub exclusions: Vec<ControlId>,
}
impl CertificationEnvelope {
#[must_use = "check the validation result"]
pub fn validate(&self) -> Result<(), Vec<CertificationRefusal>> {
let mut refusals = Vec::new();
for mapping in &self.mappings {
if mapping.framework != self.framework {
refusals.push(CertificationRefusal::UnmappedControl);
}
if !mapping.is_grounded() {
refusals.push(CertificationRefusal::UngroundedSatisfaction);
}
if self.exclusions.contains(&mapping.control) {
refusals.push(CertificationRefusal::ExcludedControlClaimed);
}
}
if refusals.is_empty() {
Ok(())
} else {
Err(refusals)
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum CertificationRefusal {
UnmappedControl,
UngroundedSatisfaction,
ExcludedControlClaimed,
}
impl core::fmt::Display for CertificationRefusal {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
let law = match self {
CertificationRefusal::UnmappedControl => "UnmappedControl",
CertificationRefusal::UngroundedSatisfaction => "UngroundedSatisfaction",
CertificationRefusal::ExcludedControlClaimed => "ExcludedControlClaimed",
};
write!(f, "certification refusal: {law}")
}
}