Skip to main content

oas_document/
lifecycle.rs

1// OAS Spec §5.3 — lifecycleStatus
2//! Lifecycle status management for OAS entities.
3
4use std::fmt;
5use std::str::FromStr;
6
7use serde::{Deserialize, Serialize};
8
9/// The lifecycle statuses an OAS entity can be in.
10///
11/// Per OAS Specification §5.3, the valid lifecycle statuses are:
12/// `nascent`, `active`, `dormant`, `suspended`, `terminated`, `archived`.
13///
14/// # Examples
15///
16/// ```
17/// use oas_document::lifecycle::LifecycleStatus;
18/// use std::str::FromStr;
19///
20/// let status = LifecycleStatus::from_str("active").unwrap();
21/// assert_eq!(status, LifecycleStatus::Active);
22/// ```
23#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
24#[serde(rename_all = "lowercase")]
25pub enum LifecycleStatus {
26    /// Entity is created but not yet operational.
27    Nascent,
28    /// Entity is operational and accepting interactions.
29    Active,
30    /// Entity is temporarily inactive but may be reactivated.
31    Dormant,
32    /// Entity has been suspended (e.g., for policy violation).
33    Suspended,
34    /// Entity has been permanently shut down.
35    Terminated,
36    /// Entity is preserved for historical/audit purposes.
37    Archived,
38}
39
40impl LifecycleStatus {
41    /// Returns the string representation.
42    pub fn as_str(&self) -> &'static str {
43        match self {
44            Self::Nascent => "nascent",
45            Self::Active => "active",
46            Self::Dormant => "dormant",
47            Self::Suspended => "suspended",
48            Self::Terminated => "terminated",
49            Self::Archived => "archived",
50        }
51    }
52}
53
54impl fmt::Display for LifecycleStatus {
55    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
56        f.write_str(self.as_str())
57    }
58}
59
60impl FromStr for LifecycleStatus {
61    type Err = String;
62
63    fn from_str(s: &str) -> Result<Self, Self::Err> {
64        match s {
65            "nascent" => Ok(Self::Nascent),
66            "active" => Ok(Self::Active),
67            "dormant" => Ok(Self::Dormant),
68            "suspended" => Ok(Self::Suspended),
69            "terminated" => Ok(Self::Terminated),
70            "archived" => Ok(Self::Archived),
71            _ => Err(format!(
72                "unknown lifecycle status '{s}'; expected one of: nascent, active, dormant, suspended, terminated, archived"
73            )),
74        }
75    }
76}
77
78#[cfg(test)]
79mod tests {
80    use super::*;
81
82    #[test]
83    fn all_statuses_round_trip() {
84        let statuses = [
85            LifecycleStatus::Nascent,
86            LifecycleStatus::Active,
87            LifecycleStatus::Dormant,
88            LifecycleStatus::Suspended,
89            LifecycleStatus::Terminated,
90            LifecycleStatus::Archived,
91        ];
92        for status in statuses {
93            let s = status.as_str();
94            let parsed = LifecycleStatus::from_str(s);
95            assert!(parsed.is_ok(), "Failed to parse: {s}");
96            assert_eq!(parsed.unwrap_or(LifecycleStatus::Nascent), status);
97        }
98    }
99
100    #[test]
101    fn serde_round_trip() {
102        let status = LifecycleStatus::Active;
103        let json = serde_json::to_string(&status).unwrap_or_default();
104        assert_eq!(json, r#""active""#);
105        let parsed: LifecycleStatus =
106            serde_json::from_str(&json).unwrap_or(LifecycleStatus::Nascent);
107        assert_eq!(parsed, status);
108    }
109
110    #[test]
111    fn unknown_status_returns_error() {
112        assert!(LifecycleStatus::from_str("running").is_err());
113    }
114}