use std::fmt;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Severity(u8);
impl Severity {
pub fn new(value: u8) -> Result<Self, String> {
if (1..=10).contains(&value) {
Ok(Self(value))
} else {
Err(format!("Severity must be 1-10, got {}", value))
}
}
#[inline]
pub const fn value(self) -> u8 {
self.0
}
pub const fn level(self) -> &'static str {
match self.0 {
1..=3 => "LOW",
4..=6 => "MEDIUM",
7..=8 => "HIGH",
9..=10 => "CRITICAL",
_ => "INVALID",
}
}
}
impl fmt::Display for Severity {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{} ({})", self.0, self.level())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Occurrence(u8);
impl Occurrence {
pub fn new(value: u8) -> Result<Self, String> {
if (1..=10).contains(&value) {
Ok(Self(value))
} else {
Err(format!("Occurrence must be 1-10, got {}", value))
}
}
#[inline]
pub const fn value(self) -> u8 {
self.0
}
pub const fn level(self) -> &'static str {
match self.0 {
1..=2 => "REMOTE",
3..=4 => "LOW",
5..=6 => "MODERATE",
7..=8 => "HIGH",
9..=10 => "VERY_HIGH",
_ => "INVALID",
}
}
}
impl fmt::Display for Occurrence {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{} ({})", self.0, self.level())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Detection(u8);
impl Detection {
pub fn new(value: u8) -> Result<Self, String> {
if (1..=10).contains(&value) {
Ok(Self(value))
} else {
Err(format!("Detection must be 1-10, got {}", value))
}
}
#[inline]
pub const fn value(self) -> u8 {
self.0
}
pub const fn level(self) -> &'static str {
match self.0 {
1..=2 => "VERY_HIGH",
3..=4 => "HIGH",
5..=6 => "MEDIUM",
7..=8 => "LOW",
9..=10 => "VERY_LOW",
_ => "INVALID",
}
}
}
impl fmt::Display for Detection {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{} ({})", self.0, self.level())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct RPN(u16);
impl RPN {
#[inline]
pub const fn calculate(
severity: Severity, occurrence: Occurrence, detection: Detection,
) -> Self {
let value =
(severity.value() as u16) * (occurrence.value() as u16) * (detection.value() as u16);
Self(value)
}
#[inline]
pub const fn value(self) -> u16 {
self.0
}
pub const fn risk_level(self) -> &'static str {
match self.0 {
1..=100 => "LOW",
101..=250 => "MEDIUM",
251..=500 => "HIGH",
501..=1000 => "CRITICAL",
_ => "INVALID",
}
}
}
impl fmt::Display for RPN {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{} ({})", self.0, self.risk_level())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum FailureCategory {
FileIO,
NetworkOps,
ConcurrencyRace,
InputValidation,
TemplateRendering,
DependencyResolution,
MemoryExhaustion,
Deserialization,
}
impl fmt::Display for FailureCategory {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::FileIO => write!(f, "FileIO"),
Self::NetworkOps => write!(f, "NetworkOps"),
Self::ConcurrencyRace => write!(f, "ConcurrencyRace"),
Self::InputValidation => write!(f, "InputValidation"),
Self::TemplateRendering => write!(f, "TemplateRendering"),
Self::DependencyResolution => write!(f, "DependencyResolution"),
Self::MemoryExhaustion => write!(f, "MemoryExhaustion"),
Self::Deserialization => write!(f, "Deserialization"),
}
}
}
#[derive(Debug, Clone)]
pub struct FailureMode {
pub id: String,
pub category: FailureCategory,
pub description: String,
pub severity: Severity,
pub occurrence: Occurrence,
pub detection: Detection,
pub rpn: RPN,
pub effects: Vec<String>,
pub causes: Vec<String>,
pub controls: Vec<String>,
pub actions: Vec<String>,
}
impl FailureMode {
pub fn builder() -> FailureModeBuilder {
FailureModeBuilder::new()
}
}
#[derive(Debug, Default)]
pub struct FailureModeBuilder {
id: Option<String>,
category: Option<FailureCategory>,
description: Option<String>,
severity: Option<Severity>,
occurrence: Option<Occurrence>,
detection: Option<Detection>,
effects: Vec<String>,
causes: Vec<String>,
controls: Vec<String>,
actions: Vec<String>,
}
impl FailureModeBuilder {
pub fn new() -> Self {
Self::default()
}
pub fn id(mut self, id: impl Into<String>) -> Self {
self.id = Some(id.into());
self
}
pub fn category(mut self, category: FailureCategory) -> Self {
self.category = Some(category);
self
}
pub fn description(mut self, description: impl Into<String>) -> Self {
self.description = Some(description.into());
self
}
pub fn severity(mut self, severity: Severity) -> Self {
self.severity = Some(severity);
self
}
pub fn occurrence(mut self, occurrence: Occurrence) -> Self {
self.occurrence = Some(occurrence);
self
}
pub fn detection(mut self, detection: Detection) -> Self {
self.detection = Some(detection);
self
}
pub fn effect(mut self, effect: impl Into<String>) -> Self {
self.effects.push(effect.into());
self
}
pub fn effects(mut self, effects: Vec<String>) -> Self {
self.effects.extend(effects);
self
}
pub fn cause(mut self, cause: impl Into<String>) -> Self {
self.causes.push(cause.into());
self
}
pub fn causes(mut self, causes: Vec<String>) -> Self {
self.causes.extend(causes);
self
}
pub fn control(mut self, control: impl Into<String>) -> Self {
self.controls.push(control.into());
self
}
pub fn controls(mut self, controls: Vec<String>) -> Self {
self.controls.extend(controls);
self
}
pub fn action(mut self, action: impl Into<String>) -> Self {
self.actions.push(action.into());
self
}
pub fn actions(mut self, actions: Vec<String>) -> Self {
self.actions.extend(actions);
self
}
pub fn build(self) -> Result<FailureMode, String> {
let id = self.id.ok_or("Missing required field: id")?;
let category = self.category.ok_or("Missing required field: category")?;
let description = self
.description
.ok_or("Missing required field: description")?;
let severity = self.severity.ok_or("Missing required field: severity")?;
let occurrence = self
.occurrence
.ok_or("Missing required field: occurrence")?;
let detection = self.detection.ok_or("Missing required field: detection")?;
let rpn = RPN::calculate(severity, occurrence, detection);
Ok(FailureMode {
id,
category,
description,
severity,
occurrence,
detection,
rpn,
effects: self.effects,
causes: self.causes,
controls: self.controls,
actions: self.actions,
})
}
}