alien_core/resources/
artifact_registry.rs1use crate::error::{ErrorData, Result};
2use crate::resource::{ResourceDefinition, ResourceOutputsDefinition, ResourceRef, ResourceType};
3use alien_error::AlienError;
4use bon::Builder;
5use serde::{Deserialize, Serialize};
6use std::any::Any;
7use std::fmt::Debug;
8
9#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Builder)]
20#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
21#[serde(rename_all = "camelCase", deny_unknown_fields)]
22#[builder(start_fn = new)]
23pub struct ArtifactRegistry {
24 #[builder(start_fn)]
27 pub id: String,
28
29 #[serde(default, skip_serializing_if = "Vec::is_empty")]
34 #[builder(default)]
35 pub replication_regions: Vec<String>,
36}
37
38impl ArtifactRegistry {
39 pub const RESOURCE_TYPE: ResourceType = ResourceType::from_static("artifact-registry");
41
42 pub fn id(&self) -> &str {
44 &self.id
45 }
46}
47
48impl ResourceDefinition for ArtifactRegistry {
50 fn get_resource_type(&self) -> ResourceType {
51 Self::RESOURCE_TYPE
52 }
53
54 fn id(&self) -> &str {
55 &self.id
56 }
57
58 fn replace_after_failed_create_is_safe(&self) -> bool {
59 false
62 }
63
64 fn get_dependencies(&self) -> Vec<ResourceRef> {
65 Vec::new()
66 }
67
68 fn validate_update(&self, new_config: &dyn ResourceDefinition) -> Result<()> {
69 let new_registry = new_config
71 .as_any()
72 .downcast_ref::<ArtifactRegistry>()
73 .ok_or_else(|| {
74 AlienError::new(ErrorData::UnexpectedResourceType {
75 resource_id: self.id.clone(),
76 expected: Self::RESOURCE_TYPE,
77 actual: new_config.get_resource_type(),
78 })
79 })?;
80
81 if self.id != new_registry.id {
82 return Err(AlienError::new(ErrorData::InvalidResourceUpdate {
83 resource_id: self.id.clone(),
84 reason: "the 'id' field is immutable".to_string(),
85 }));
86 }
87 Ok(())
88 }
89
90 fn as_any(&self) -> &dyn Any {
91 self
92 }
93
94 fn as_any_mut(&mut self) -> &mut dyn Any {
95 self
96 }
97
98 fn box_clone(&self) -> Box<dyn ResourceDefinition> {
99 Box::new(self.clone())
100 }
101
102 fn resource_eq(&self, other: &dyn ResourceDefinition) -> bool {
103 other.as_any().downcast_ref::<ArtifactRegistry>() == Some(self)
104 }
105
106 fn to_json_value(&self) -> serde_json::Result<serde_json::Value> {
107 serde_json::to_value(self)
108 }
109}
110
111#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
113#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
114#[serde(rename_all = "camelCase")]
115pub struct ArtifactRegistryOutputs {
116 pub registry_id: String,
121
122 pub registry_endpoint: String,
127
128 #[serde(skip_serializing_if = "Option::is_none")]
133 pub pull_role: Option<String>,
134
135 #[serde(skip_serializing_if = "Option::is_none")]
140 pub push_role: Option<String>,
141}
142
143impl ResourceOutputsDefinition for ArtifactRegistryOutputs {
144 fn get_resource_type(&self) -> ResourceType {
145 ArtifactRegistry::RESOURCE_TYPE.clone()
146 }
147
148 fn as_any(&self) -> &dyn Any {
149 self
150 }
151
152 fn box_clone(&self) -> Box<dyn ResourceOutputsDefinition> {
153 Box::new(self.clone())
154 }
155
156 fn outputs_eq(&self, other: &dyn ResourceOutputsDefinition) -> bool {
157 other.as_any().downcast_ref::<ArtifactRegistryOutputs>() == Some(self)
158 }
159
160 fn to_json_value(&self) -> serde_json::Result<serde_json::Value> {
161 serde_json::to_value(self)
162 }
163}
164
165#[cfg(test)]
166mod tests {
167 use super::*;
168
169 #[test]
170 fn test_artifact_registry_creation() {
171 let registry = ArtifactRegistry::new("my-registry".to_string()).build();
172 assert_eq!(registry.id, "my-registry");
173 }
174
175 #[test]
176 fn test_artifact_registry_dependencies() {
177 let registry = ArtifactRegistry::new("my-registry".to_string()).build();
178 let dependencies = registry.get_dependencies();
179 assert!(dependencies.is_empty());
180 }
181}