Skip to main content

planspec_core/
resource.rs

1//! Polymorphic resource handling.
2
3use serde::{Deserialize, Serialize};
4use serde_json::Value;
5
6use crate::types::binding::Binding;
7use crate::types::capability::Capability;
8use crate::types::execution::Execution;
9use crate::types::gate::Gate;
10use crate::types::goal::Goal;
11use crate::types::meta::ObjectMeta;
12use crate::types::plan::Plan;
13
14/// Resource is an enum that can hold any PlanSpec resource type.
15///
16/// Uses untagged serialization since each resource type has its own `kind` field.
17#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
18#[serde(untagged)]
19pub enum Resource {
20    /// A Goal resource.
21    Goal(Goal),
22    /// A Plan resource.
23    Plan(Plan),
24    /// A Capability resource.
25    Capability(Capability),
26    /// A Binding resource.
27    Binding(Binding),
28    /// An Execution resource.
29    Execution(Execution),
30    /// A Gate resource.
31    Gate(Gate),
32}
33
34impl Resource {
35    /// Get the kind of this resource.
36    pub fn kind(&self) -> &str {
37        match self {
38            Resource::Goal(_) => "Goal",
39            Resource::Plan(_) => "Plan",
40            Resource::Capability(_) => "Capability",
41            Resource::Binding(_) => "Binding",
42            Resource::Execution(_) => "Execution",
43            Resource::Gate(_) => "Gate",
44        }
45    }
46
47    /// Get the metadata of this resource.
48    pub fn metadata(&self) -> &ObjectMeta {
49        match self {
50            Resource::Goal(r) => &r.metadata,
51            Resource::Plan(r) => &r.metadata,
52            Resource::Capability(r) => &r.metadata,
53            Resource::Binding(r) => &r.metadata,
54            Resource::Execution(r) => &r.metadata,
55            Resource::Gate(r) => &r.metadata,
56        }
57    }
58
59    /// Get a mutable reference to the metadata of this resource.
60    pub fn metadata_mut(&mut self) -> &mut ObjectMeta {
61        match self {
62            Resource::Goal(r) => &mut r.metadata,
63            Resource::Plan(r) => &mut r.metadata,
64            Resource::Capability(r) => &mut r.metadata,
65            Resource::Binding(r) => &mut r.metadata,
66            Resource::Execution(r) => &mut r.metadata,
67            Resource::Gate(r) => &mut r.metadata,
68        }
69    }
70
71    /// Get the name of this resource.
72    pub fn name(&self) -> &str {
73        &self.metadata().name
74    }
75
76    /// Get the namespace of this resource.
77    pub fn namespace(&self) -> &str {
78        &self.metadata().namespace
79    }
80
81    /// Get the API version of this resource.
82    pub fn api_version(&self) -> &str {
83        match self {
84            Resource::Goal(r) => &r.api_version,
85            Resource::Plan(r) => &r.api_version,
86            Resource::Capability(r) => &r.api_version,
87            Resource::Binding(r) => &r.api_version,
88            Resource::Execution(r) => &r.api_version,
89            Resource::Gate(r) => &r.api_version,
90        }
91    }
92}
93
94impl From<Goal> for Resource {
95    fn from(goal: Goal) -> Self {
96        Resource::Goal(goal)
97    }
98}
99
100impl From<Plan> for Resource {
101    fn from(plan: Plan) -> Self {
102        Resource::Plan(plan)
103    }
104}
105
106impl From<Capability> for Resource {
107    fn from(capability: Capability) -> Self {
108        Resource::Capability(capability)
109    }
110}
111
112impl From<Binding> for Resource {
113    fn from(binding: Binding) -> Self {
114        Resource::Binding(binding)
115    }
116}
117
118impl From<Execution> for Resource {
119    fn from(execution: Execution) -> Self {
120        Resource::Execution(execution)
121    }
122}
123
124impl From<Gate> for Resource {
125    fn from(gate: Gate) -> Self {
126        Resource::Gate(gate)
127    }
128}
129
130impl TryFrom<Value> for Resource {
131    type Error = ResourceParseError;
132
133    fn try_from(value: Value) -> Result<Self, Self::Error> {
134        let kind = value
135            .get("kind")
136            .and_then(|k| k.as_str())
137            .ok_or(ResourceParseError::MissingKind)?;
138
139        match kind {
140            "Goal" => {
141                let goal: Goal = serde_json::from_value(value)
142                    .map_err(|e| ResourceParseError::DeserializationError(e.to_string()))?;
143                Ok(Resource::Goal(goal))
144            }
145            "Plan" => {
146                let plan: Plan = serde_json::from_value(value)
147                    .map_err(|e| ResourceParseError::DeserializationError(e.to_string()))?;
148                Ok(Resource::Plan(plan))
149            }
150            "Capability" => {
151                let capability: Capability = serde_json::from_value(value)
152                    .map_err(|e| ResourceParseError::DeserializationError(e.to_string()))?;
153                Ok(Resource::Capability(capability))
154            }
155            "Binding" => {
156                let binding: Binding = serde_json::from_value(value)
157                    .map_err(|e| ResourceParseError::DeserializationError(e.to_string()))?;
158                Ok(Resource::Binding(binding))
159            }
160            "Execution" => {
161                let execution: Execution = serde_json::from_value(value)
162                    .map_err(|e| ResourceParseError::DeserializationError(e.to_string()))?;
163                Ok(Resource::Execution(execution))
164            }
165            "Gate" => {
166                let gate: Gate = serde_json::from_value(value)
167                    .map_err(|e| ResourceParseError::DeserializationError(e.to_string()))?;
168                Ok(Resource::Gate(gate))
169            }
170            _ => Err(ResourceParseError::UnknownKind(kind.to_string())),
171        }
172    }
173}
174
175/// Error parsing a resource from JSON.
176#[derive(Debug, Clone, thiserror::Error)]
177pub enum ResourceParseError {
178    /// The JSON is missing the "kind" field.
179    #[error("missing 'kind' field")]
180    MissingKind,
181
182    /// The kind is not recognized.
183    #[error("unknown resource kind: {0}")]
184    UnknownKind(String),
185
186    /// Failed to deserialize the resource.
187    #[error("deserialization error: {0}")]
188    DeserializationError(String),
189}