continuity-runtime 0.1.0

Typed continuity and incident surface crate with bounded recovery profiles; not an incident-command runtime
Documentation
use crate::error::{ContinuityValidationError, ContinuityValidationResult};
use crate::validation::{require_non_empty, require_non_empty_slice, require_schema_version};
use crate::vocab::{IncidentSeverityV1, IncidentStatusV1};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
pub struct IncidentCaseV1 {
    pub schema_version: String,
    pub incident_case_id: String,
    pub service_level_profile_id: String,
    pub severity: IncidentSeverityV1,
    pub opened_at: String,
    pub summary: String,
    pub commander_ref: String,
    pub status: IncidentStatusV1,
}

impl IncidentCaseV1 {
    pub const SCHEMA_VERSION: &'static str = "IncidentCaseV1";

    pub fn new(
        incident_case_id: impl Into<String>,
        service_level_profile_id: impl Into<String>,
        severity: IncidentSeverityV1,
        opened_at: impl Into<String>,
        summary: impl Into<String>,
        commander_ref: impl Into<String>,
        status: IncidentStatusV1,
    ) -> Result<Self, ContinuityValidationError> {
        let value = Self {
            schema_version: Self::SCHEMA_VERSION.to_string(),
            incident_case_id: incident_case_id.into(),
            service_level_profile_id: service_level_profile_id.into(),
            severity,
            opened_at: opened_at.into(),
            summary: summary.into(),
            commander_ref: commander_ref.into(),
            status,
        };
        value.validate()?;
        Ok(value)
    }

    pub fn validate(&self) -> ContinuityValidationResult {
        require_schema_version(&self.schema_version, Self::SCHEMA_VERSION)?;
        require_non_empty(&self.incident_case_id, "incident_case_id")?;
        require_non_empty(&self.service_level_profile_id, "service_level_profile_id")?;
        require_non_empty(&self.opened_at, "opened_at")?;
        require_non_empty(&self.summary, "summary")?;
        require_non_empty(&self.commander_ref, "commander_ref")?;
        Ok(())
    }
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
pub struct ContainmentDecisionV1 {
    pub schema_version: String,
    pub containment_decision_id: String,
    pub incident_case_id: String,
    pub decision: String,
    pub justification: String,
    pub approved_by: Vec<String>,
    pub generated_at: String,
}

impl ContainmentDecisionV1 {
    pub const SCHEMA_VERSION: &'static str = "ContainmentDecisionV1";

    pub fn new(
        containment_decision_id: impl Into<String>,
        incident_case_id: impl Into<String>,
        decision: impl Into<String>,
        justification: impl Into<String>,
        approved_by: Vec<String>,
        generated_at: impl Into<String>,
    ) -> Result<Self, ContinuityValidationError> {
        let value = Self {
            schema_version: Self::SCHEMA_VERSION.to_string(),
            containment_decision_id: containment_decision_id.into(),
            incident_case_id: incident_case_id.into(),
            decision: decision.into(),
            justification: justification.into(),
            approved_by,
            generated_at: generated_at.into(),
        };
        value.validate()?;
        Ok(value)
    }

    pub fn validate(&self) -> ContinuityValidationResult {
        require_schema_version(&self.schema_version, Self::SCHEMA_VERSION)?;
        require_non_empty(&self.containment_decision_id, "containment_decision_id")?;
        require_non_empty(&self.incident_case_id, "incident_case_id")?;
        require_non_empty(&self.decision, "decision")?;
        require_non_empty(&self.justification, "justification")?;
        require_non_empty_slice(&self.approved_by, "approved_by")?;
        require_non_empty(&self.generated_at, "generated_at")?;
        Ok(())
    }
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
pub struct ForensicFreezeV1 {
    pub schema_version: String,
    pub forensic_freeze_id: String,
    pub incident_case_id: String,
    pub frozen_surfaces: Vec<String>,
    pub retention_hold_until: String,
    pub initiated_at: String,
}

impl ForensicFreezeV1 {
    pub const SCHEMA_VERSION: &'static str = "ForensicFreezeV1";

    pub fn new(
        forensic_freeze_id: impl Into<String>,
        incident_case_id: impl Into<String>,
        frozen_surfaces: Vec<String>,
        retention_hold_until: impl Into<String>,
        initiated_at: impl Into<String>,
    ) -> Result<Self, ContinuityValidationError> {
        let value = Self {
            schema_version: Self::SCHEMA_VERSION.to_string(),
            forensic_freeze_id: forensic_freeze_id.into(),
            incident_case_id: incident_case_id.into(),
            frozen_surfaces,
            retention_hold_until: retention_hold_until.into(),
            initiated_at: initiated_at.into(),
        };
        value.validate()?;
        Ok(value)
    }

    pub fn validate(&self) -> ContinuityValidationResult {
        require_schema_version(&self.schema_version, Self::SCHEMA_VERSION)?;
        require_non_empty(&self.forensic_freeze_id, "forensic_freeze_id")?;
        require_non_empty(&self.incident_case_id, "incident_case_id")?;
        require_non_empty_slice(&self.frozen_surfaces, "frozen_surfaces")?;
        require_non_empty(&self.retention_hold_until, "retention_hold_until")?;
        require_non_empty(&self.initiated_at, "initiated_at")?;
        Ok(())
    }
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
pub struct ContinuityExceptionV1 {
    pub schema_version: String,
    pub continuity_exception_id: String,
    pub incident_case_id: String,
    pub exception_kind: String,
    pub reason: String,
    pub expires_at: String,
    pub post_hoc_review_required: bool,
}

impl ContinuityExceptionV1 {
    pub const SCHEMA_VERSION: &'static str = "ContinuityExceptionV1";

    pub fn new(
        continuity_exception_id: impl Into<String>,
        incident_case_id: impl Into<String>,
        exception_kind: impl Into<String>,
        reason: impl Into<String>,
        expires_at: impl Into<String>,
        post_hoc_review_required: bool,
    ) -> Result<Self, ContinuityValidationError> {
        let value = Self {
            schema_version: Self::SCHEMA_VERSION.to_string(),
            continuity_exception_id: continuity_exception_id.into(),
            incident_case_id: incident_case_id.into(),
            exception_kind: exception_kind.into(),
            reason: reason.into(),
            expires_at: expires_at.into(),
            post_hoc_review_required,
        };
        value.validate()?;
        Ok(value)
    }

    pub fn validate(&self) -> ContinuityValidationResult {
        require_schema_version(&self.schema_version, Self::SCHEMA_VERSION)?;
        require_non_empty(&self.continuity_exception_id, "continuity_exception_id")?;
        require_non_empty(&self.incident_case_id, "incident_case_id")?;
        require_non_empty(&self.exception_kind, "exception_kind")?;
        require_non_empty(&self.reason, "reason")?;
        require_non_empty(&self.expires_at, "expires_at")?;
        Ok(())
    }
}