use serde::{Deserialize, Serialize};
use tatara_env::compile::Resource;
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
pub struct ResourceId {
pub keyword: String,
pub name: String,
}
impl ResourceId {
#[must_use]
pub fn from_resource(r: &Resource) -> Self {
let obj = r.value.as_object();
let name = obj
.and_then(|o| o.get("name"))
.and_then(|v| v.as_str())
.or_else(|| {
obj.and_then(|o| {
if r.keyword == "defgateway" {
o.get("gateway_class_name").and_then(|v| v.as_str())
} else {
None
}
})
})
.unwrap_or("<unnamed>")
.to_string();
Self {
keyword: r.keyword.clone(),
name,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(tag = "kind", rename_all = "kebab-case")]
pub enum Change {
Add {
id: ResourceId,
value: serde_json::Value,
},
Remove { id: ResourceId },
Change {
id: ResourceId,
old_blake3: String,
new_blake3: String,
new_value: serde_json::Value,
},
}
impl Change {
#[must_use]
pub fn id(&self) -> &ResourceId {
match self {
Self::Add { id, .. } | Self::Remove { id } | Self::Change { id, .. } => id,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
pub struct Plan {
pub adds: Vec<Change>,
pub removes: Vec<Change>,
pub changes: Vec<Change>,
pub unchanged: Vec<ResourceId>,
}
impl Plan {
#[must_use]
pub fn actionable_count(&self) -> usize {
self.adds.len() + self.removes.len() + self.changes.len()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.actionable_count() == 0
}
pub fn iter_actionable(&self) -> impl Iterator<Item = &Change> {
self.removes
.iter()
.chain(self.adds.iter())
.chain(self.changes.iter())
}
}