1#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
3#[cfg_attr(feature = "serde", derive(serde::Deserialize))]
4pub struct DefinitionPath(pub String);
5
6impl std::borrow::Borrow<str> for DefinitionPath {
7 fn borrow(&self) -> &str {
8 &self.0
9 }
10}
11
12impl std::ops::Deref for DefinitionPath {
13 type Target = str;
14
15 fn deref(&self) -> &Self::Target {
16 &self.0
17 }
18}
19
20impl std::fmt::Display for DefinitionPath {
21 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
22 self.0.fmt(f)
23 }
24}
25
26#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
28pub enum IntegerFormat {
29 Int32,
30 Int64,
31}
32
33#[derive(Clone, Copy, Debug, Eq, PartialEq, Default, PartialOrd, Ord)]
35#[cfg_attr(feature = "serde", derive(serde::Deserialize))]
36pub enum KubernetesListType {
37 #[default]
38 #[cfg_attr(feature = "serde", serde(rename = "atomic"))]
39 Atomic,
40
41 #[cfg_attr(feature = "serde", serde(rename = "map"))]
42 Map,
43
44 #[cfg_attr(feature = "serde", serde(rename = "set"))]
45 Set,
46}
47
48#[derive(Clone, Copy, Debug, Eq, PartialEq, Default, PartialOrd, Ord)]
50#[cfg_attr(feature = "serde", derive(serde::Deserialize))]
51pub enum KubernetesMapType {
52 #[cfg_attr(feature = "serde", serde(rename = "atomic"))]
53 Atomic,
54
55 #[default]
56 #[cfg_attr(feature = "serde", serde(rename = "granular"))]
57 Granular,
58}
59
60#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
61pub enum MergeType {
62 Default,
63
64 List {
65 strategy: KubernetesListType,
66 keys: Vec<String>,
67 item_merge_type: Box<MergeType>,
68 },
69
70 Map {
71 strategy: KubernetesMapType,
72 value_merge_type: Box<MergeType>,
73 },
74}
75
76#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
78pub enum NumberFormat {
79 Double,
80}
81
82#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
84#[cfg_attr(feature = "serde", derive(serde::Deserialize))]
85pub struct PropertyName(pub String);
86
87impl std::borrow::Borrow<str> for PropertyName {
88 fn borrow(&self) -> &str {
89 &self.0
90 }
91}
92
93impl std::ops::Deref for PropertyName {
94 type Target = str;
95
96 fn deref(&self) -> &Self::Target {
97 &self.0
98 }
99}
100
101impl std::fmt::Display for PropertyName {
102 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
103 self.0.fmt(f)
104 }
105}
106
107#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
109pub struct RefPath {
110 pub path: String,
111 pub can_be_default: Option<bool>,
112}
113
114#[cfg(feature = "serde")]
115impl<'de> serde::Deserialize<'de> for RefPath {
116 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: serde::Deserializer<'de> {
117 let path: String = serde::Deserialize::deserialize(deserializer)?;
118 let mut parts = path.split('/');
119
120 if parts.next() != Some("#") {
121 return Err(serde::de::Error::invalid_value(serde::de::Unexpected::Str(&path), &"path like `#/definitions/$definitionName`"));
122 }
123
124 if parts.next() != Some("definitions") {
125 return Err(serde::de::Error::invalid_value(serde::de::Unexpected::Str(&path), &"path like `#/definitions/$definitionName`"));
126 }
127
128 let ref_path = parts.next().ok_or_else(|| serde::de::Error::invalid_value(serde::de::Unexpected::Str(&path), &"path like `#/definitions/$definitionName`"))?;
129
130 if parts.next().is_some() {
131 return Err(serde::de::Error::invalid_value(serde::de::Unexpected::Str(&path), &"path like `#/definitions/$definitionName`"));
132 }
133
134 Ok(RefPath {
135 path: ref_path.to_string(),
136 can_be_default: None,
137 })
138 }
139}
140
141impl RefPath {
142 pub(crate) fn references_scope(&self, map_namespace: &impl crate::MapNamespace) -> bool {
143 let path_parts: Vec<_> = self.path.split('.').collect();
144 map_namespace.map_namespace(&path_parts[..(path_parts.len() - 1)]).is_none()
145 }
146}
147
148#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
150pub struct Schema {
151 pub description: Option<String>,
152 pub kind: SchemaKind,
153 pub kubernetes_group_kind_versions: Vec<super::KubernetesGroupKindVersion>,
154
155 pub merge_type: MergeType,
156
157 pub list_kind: Option<String>,
159
160 pub impl_deep_merge: bool,
162}
163
164#[cfg(feature = "serde")]
165#[allow(clippy::use_self)]
166impl<'de> serde::Deserialize<'de> for Schema {
167 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: serde::Deserializer<'de> {
168 #[derive(Debug, serde::Deserialize)]
169 struct InnerSchema {
170 #[serde(rename = "additionalProperties")]
171 additional_properties: Option<Box<Schema>>,
172
173 description: Option<String>,
174
175 format: Option<String>,
176
177 items: Option<Box<Schema>>,
178
179 #[serde(default, rename = "x-kubernetes-group-version-kind")]
180 kubernetes_group_kind_versions: Vec<super::KubernetesGroupKindVersion>,
181
182 #[serde(default, rename = "x-kubernetes-list-map-keys")]
183 kubernetes_list_map_keys: Vec<String>,
184
185 #[serde(default, rename = "x-kubernetes-list-type")]
186 kubernetes_list_type: KubernetesListType,
187
188 #[serde(default, rename = "x-kubernetes-map-type")]
189 kubernetes_map_type: KubernetesMapType,
190
191 #[serde(default, rename = "x-kubernetes-patch-merge-key")]
192 kubernetes_patch_merge_key: Option<String>,
193
194 #[serde(default, rename = "x-kubernetes-patch-strategy")]
197 kubernetes_patch_strategy: String,
198
199 properties: Option<std::collections::BTreeMap<PropertyName, Schema>>,
200
201 #[serde(rename = "$ref")]
202 ref_path: Option<RefPath>,
203
204 #[serde(default)]
205 required: Vec<PropertyName>,
206
207 #[serde(rename = "type")]
208 ty: Option<String>,
209 }
210
211 let mut value: InnerSchema = serde::Deserialize::deserialize(deserializer)?;
212
213 let kind =
214 if let Some(ref_path) = value.ref_path {
215 SchemaKind::Ref(ref_path)
216 }
217 else if let Some(properties) = value.properties.take() {
218 if value.ty.as_deref() != Some("object") {
219 return Err(serde::de::Error::custom(format!("schema has properties but not type=object {value:?}")));
220 }
221
222 let required: std::collections::BTreeSet<_> = value.required.into_iter().collect();
223 SchemaKind::Properties(properties.into_iter().map(|(name, schema)| {
224 let required = required.contains(&name);
225 (name, (schema, required))
226 }).collect())
227 }
228 else if let Some(ty) = value.ty {
229 SchemaKind::Ty(Type::parse::<D>(
230 &ty,
231 value.additional_properties,
232 value.format.as_deref(),
233 value.items,
234 )?)
235 }
236 else {
237 SchemaKind::Ty(Type::Any)
238 };
239
240 if let Some(key) = value.kubernetes_patch_merge_key {
241 value.kubernetes_list_map_keys = vec![key];
242 }
243 if value.kubernetes_patch_strategy.split(',').any(|x| x == "merge") {
244 value.kubernetes_list_type =
245 if value.kubernetes_list_map_keys.is_empty() {
246 KubernetesListType::Set
247 }
248 else {
249 KubernetesListType::Map
250 };
251 }
252
253 let merge_type = match &kind {
254 SchemaKind::Ty(Type::Array { items }) => MergeType::List {
255 strategy: value.kubernetes_list_type,
256 keys: value.kubernetes_list_map_keys,
257 item_merge_type: Box::new(items.merge_type.clone()),
258 },
259
260 SchemaKind::Ty(Type::Object { additional_properties }) => MergeType::Map {
261 strategy: value.kubernetes_map_type,
262 value_merge_type: Box::new(additional_properties.merge_type.clone()),
263 },
264
265 _ => MergeType::Default,
266 };
267
268 Ok(Schema {
269 description: value.description,
270 kind,
271 kubernetes_group_kind_versions: value.kubernetes_group_kind_versions,
272 list_kind: None,
273 merge_type,
274 impl_deep_merge: true,
275 })
276 }
277}
278
279#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
281pub enum SchemaKind {
282 Properties(std::collections::BTreeMap<PropertyName, (Schema, bool)>),
283 Ref(RefPath),
284 Ty(Type),
285}
286
287#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
289pub enum StringFormat {
290 Byte,
291 DateTime,
292}
293
294#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
296pub enum Type {
297 Any,
298 Array { items: Box<Schema> },
299 Boolean,
300 Integer { format: IntegerFormat },
301 Number { format: NumberFormat },
302 Object { additional_properties: Box<Schema> },
303 String { format: Option<StringFormat> },
304
305 CustomResourceSubresources(String),
307
308 IntOrString,
310 JsonSchemaPropsOr(&'static str, JsonSchemaPropsOr),
311
312 Patch,
313 WatchEvent(RefPath),
314
315 ListDef { metadata: Box<SchemaKind> }, ListRef { items: Box<SchemaKind> }, }
319
320#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
321pub enum JsonSchemaPropsOr {
322 Array,
323 Bool,
324 StringArray,
325}
326
327impl Type {
328 #[cfg(feature = "serde")]
329 pub(crate) fn parse<'de, D>(
330 ty: &str,
331 additional_properties: Option<Box<Schema>>,
332 format: Option<&str>,
333 items: Option<Box<Schema>>,
334 ) -> Result<Self, D::Error> where D: serde::Deserializer<'de> {
335 match ty {
336 "array" => Ok(Type::Array {
337 items: items.ok_or_else(|| serde::de::Error::missing_field("items"))?,
338 }),
339
340 "boolean" => Ok(Type::Boolean),
341
342 "integer" => {
343 let format = match format {
344 Some("int32") => IntegerFormat::Int32,
345 Some("int64") | None => IntegerFormat::Int64,
346 Some(format) => return Err(serde::de::Error::invalid_value(serde::de::Unexpected::Str(format), &"one of int32, int64")),
347 };
348 Ok(Type::Integer { format })
349 },
350
351 "number" => {
352 let format = format.ok_or_else(|| serde::de::Error::missing_field("format"))?;
353 let format = match format {
354 "double" => NumberFormat::Double,
355 format => return Err(serde::de::Error::invalid_value(serde::de::Unexpected::Str(format), &"one of double")),
356 };
357 Ok(Type::Number { format })
358 },
359
360 "object" => match additional_properties {
361 Some(additional_properties) => Ok(Type::Object { additional_properties }),
362 None => Ok(Type::Any),
363 },
364
365 "string" => {
366 let format = match format {
367 Some("byte") => Some(StringFormat::Byte),
368 Some("date-time") => Some(StringFormat::DateTime),
369 Some("int-or-string") => return Ok(Type::IntOrString),
370 Some(format) => return Err(serde::de::Error::invalid_value(serde::de::Unexpected::Str(format), &"one of byte, date-time, int-or-string")),
371 None => None,
372 };
373 Ok(Type::String { format })
374 },
375
376 s => Err(serde::de::Error::invalid_value(serde::de::Unexpected::Str(s), &"one of array, boolean, integer, number, object, string")),
377 }
378 }
379}