oas-document 1.1.0

OAS Identity Document types and validation for the Open Agent Specification
Documentation
// OAS Spec §5.3 — lifecycleStatus
//! Lifecycle status management for OAS entities.

use std::fmt;
use std::str::FromStr;

use serde::{Deserialize, Serialize};

/// The lifecycle statuses an OAS entity can be in.
///
/// Per OAS Specification §5.3, the valid lifecycle statuses are:
/// `nascent`, `active`, `dormant`, `suspended`, `terminated`, `archived`.
///
/// # Examples
///
/// ```
/// use oas_document::lifecycle::LifecycleStatus;
/// use std::str::FromStr;
///
/// let status = LifecycleStatus::from_str("active").unwrap();
/// assert_eq!(status, LifecycleStatus::Active);
/// ```
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum LifecycleStatus {
    /// Entity is created but not yet operational.
    Nascent,
    /// Entity is operational and accepting interactions.
    Active,
    /// Entity is temporarily inactive but may be reactivated.
    Dormant,
    /// Entity has been suspended (e.g., for policy violation).
    Suspended,
    /// Entity has been permanently shut down.
    Terminated,
    /// Entity is preserved for historical/audit purposes.
    Archived,
}

impl LifecycleStatus {
    /// Returns the string representation.
    pub fn as_str(&self) -> &'static str {
        match self {
            Self::Nascent => "nascent",
            Self::Active => "active",
            Self::Dormant => "dormant",
            Self::Suspended => "suspended",
            Self::Terminated => "terminated",
            Self::Archived => "archived",
        }
    }
}

impl fmt::Display for LifecycleStatus {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.write_str(self.as_str())
    }
}

impl FromStr for LifecycleStatus {
    type Err = String;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "nascent" => Ok(Self::Nascent),
            "active" => Ok(Self::Active),
            "dormant" => Ok(Self::Dormant),
            "suspended" => Ok(Self::Suspended),
            "terminated" => Ok(Self::Terminated),
            "archived" => Ok(Self::Archived),
            _ => Err(format!(
                "unknown lifecycle status '{s}'; expected one of: nascent, active, dormant, suspended, terminated, archived"
            )),
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn all_statuses_round_trip() {
        let statuses = [
            LifecycleStatus::Nascent,
            LifecycleStatus::Active,
            LifecycleStatus::Dormant,
            LifecycleStatus::Suspended,
            LifecycleStatus::Terminated,
            LifecycleStatus::Archived,
        ];
        for status in statuses {
            let s = status.as_str();
            let parsed = LifecycleStatus::from_str(s);
            assert!(parsed.is_ok(), "Failed to parse: {s}");
            assert_eq!(parsed.unwrap_or(LifecycleStatus::Nascent), status);
        }
    }

    #[test]
    fn serde_round_trip() {
        let status = LifecycleStatus::Active;
        let json = serde_json::to_string(&status).unwrap_or_default();
        assert_eq!(json, r#""active""#);
        let parsed: LifecycleStatus =
            serde_json::from_str(&json).unwrap_or(LifecycleStatus::Nascent);
        assert_eq!(parsed, status);
    }

    #[test]
    fn unknown_status_returns_error() {
        assert!(LifecycleStatus::from_str("running").is_err());
    }
}