cdk_from_cfn/parser/resource/
mod.rs1use crate::primitives::WrapperF64;
2use crate::Hasher;
3use indexmap::map::Entry;
4use indexmap::IndexMap;
5use serde::de::Error;
6use serde::{de, Deserialize, Deserializer};
7use std::convert::TryInto;
8use std::fmt;
9use std::marker::PhantomData;
10
11pub use super::intrinsics::IntrinsicFunction;
12
13#[derive(Clone, Debug, PartialEq)]
14pub enum ResourceValue {
15 Null,
16 Bool(bool),
17 Number(i64),
18 Double(WrapperF64),
19 String(String),
20 Array(Vec<ResourceValue>),
21 Object(IndexMap<String, ResourceValue, Hasher>),
22
23 IntrinsicFunction(Box<IntrinsicFunction>),
24}
25
26impl From<IntrinsicFunction> for ResourceValue {
27 fn from(i: IntrinsicFunction) -> Self {
28 match i {
29 IntrinsicFunction::Ref(ref_name) if ref_name == "AWS::NoValue" => ResourceValue::Null,
30 i => ResourceValue::IntrinsicFunction(Box::new(i)),
31 }
32 }
33}
34
35impl<'de> serde::de::Deserialize<'de> for ResourceValue {
36 fn deserialize<D: serde::de::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
37 struct ResourceValueVisitor;
38 impl<'de> serde::de::Visitor<'de> for ResourceValueVisitor {
39 type Value = ResourceValue;
40
41 #[inline]
42 fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
43 formatter.write_str("a CloudFormation resource value")
44 }
45
46 #[inline]
47 fn visit_bool<E: serde::de::Error>(self, val: bool) -> Result<Self::Value, E> {
48 Ok(Self::Value::Bool(val))
49 }
50
51 #[inline]
52 fn visit_enum<A: serde::de::EnumAccess<'de>>(
53 self,
54 data: A,
55 ) -> Result<Self::Value, A::Error> {
56 IntrinsicFunction::from_enum(data).map(Into::into)
57 }
58
59 #[inline]
60 fn visit_f64<E: serde::de::Error>(self, val: f64) -> Result<Self::Value, E> {
61 Ok(Self::Value::Double(val.into()))
62 }
63
64 #[inline]
65 fn visit_i64<E: serde::de::Error>(self, val: i64) -> Result<Self::Value, E> {
66 Ok(Self::Value::Number(val))
67 }
68
69 #[cold]
70 fn visit_i128<E: serde::de::Error>(self, val: i128) -> Result<Self::Value, E> {
71 if let Ok(val) = val.try_into() {
72 Ok(Self::Value::Number(val))
73 } else {
74 Ok(Self::Value::Double(val.into()))
75 }
76 }
77
78 fn visit_map<A: serde::de::MapAccess<'de>>(
79 self,
80 mut data: A,
81 ) -> Result<Self::Value, A::Error> {
82 let mut map = IndexMap::with_capacity_and_hasher(
83 data.size_hint().unwrap_or_default(),
84 Hasher::default(),
85 );
86 while let Some(key) = data.next_key::<String>()? {
87 if let Some(intrinsic) = IntrinsicFunction::from_singleton_map(&key, &mut data)?
88 {
89 if let Some(extraneous) = data.next_key()? {
90 return Err(A::Error::unknown_field(extraneous, &[]));
91 }
92 return Ok(intrinsic.into());
93 }
94 match map.entry(key) {
95 Entry::Vacant(entry) => {
96 entry.insert(data.next_value()?);
97 }
98 Entry::Occupied(entry) => {
99 return Err(A::Error::custom(&format!(
100 "duplicate object key {key:?}",
101 key = entry.key()
102 )))
103 }
104 }
105 }
106 Ok(Self::Value::Object(map))
107 }
108
109 fn visit_seq<A: serde::de::SeqAccess<'de>>(
110 self,
111 mut data: A,
112 ) -> Result<Self::Value, A::Error> {
113 let mut vec = Vec::with_capacity(data.size_hint().unwrap_or_default());
114 while let Some(elem) = data.next_element()? {
115 vec.push(elem);
116 }
117 Ok(Self::Value::Array(vec))
118 }
119
120 #[inline]
121 fn visit_str<E: serde::de::Error>(self, val: &str) -> Result<Self::Value, E> {
122 Ok(Self::Value::String(val.into()))
123 }
124
125 #[inline]
126 fn visit_u64<E: serde::de::Error>(self, val: u64) -> Result<Self::Value, E> {
127 if let Ok(val) = val.try_into() {
128 Ok(Self::Value::Number(val))
129 } else {
130 Ok(Self::Value::Double(val.into()))
131 }
132 }
133
134 #[cold]
135 fn visit_u128<E: serde::de::Error>(self, val: u128) -> Result<Self::Value, E> {
136 if let Ok(val) = val.try_into() {
137 Ok(Self::Value::Number(val))
138 } else {
139 Ok(Self::Value::Double(val.into()))
140 }
141 }
142
143 #[inline]
144 fn visit_unit<E: serde::de::Error>(self) -> Result<Self::Value, E> {
145 Ok(Self::Value::Null)
146 }
147 }
148
149 deserializer.deserialize_any(ResourceValueVisitor)
150 }
151}
152
153#[derive(Debug, PartialEq, serde::Deserialize)]
154#[serde(rename_all = "PascalCase")]
155pub struct ResourceAttributes {
156 #[serde(rename = "Type")]
157 pub resource_type: String,
158
159 pub condition: Option<String>,
160
161 pub metadata: Option<ResourceValue>,
162
163 #[serde(deserialize_with = "string_or_seq_string")]
164 #[serde(default)]
165 pub depends_on: Vec<String>,
166
167 pub update_policy: Option<ResourceValue>,
168
169 pub deletion_policy: Option<DeletionPolicy>,
170
171 #[serde(default)]
172 pub properties: IndexMap<String, ResourceValue>,
173}
174
175fn string_or_seq_string<'de, D>(deserializer: D) -> Result<Vec<String>, D::Error>
176where
177 D: Deserializer<'de>,
178{
179 struct StringOrVec(PhantomData<Vec<String>>);
180
181 impl<'de> de::Visitor<'de> for StringOrVec {
182 type Value = Vec<String>;
183
184 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
185 formatter.write_str("string or list of strings")
186 }
187
188 fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
189 where
190 E: de::Error,
191 {
192 Ok(vec![value.to_owned()])
193 }
194
195 fn visit_seq<S>(self, visitor: S) -> Result<Self::Value, S::Error>
196 where
197 S: de::SeqAccess<'de>,
198 {
199 Deserialize::deserialize(de::value::SeqAccessDeserializer::new(visitor))
200 }
201 }
202
203 deserializer.deserialize_any(StringOrVec(PhantomData))
204}
205
206#[derive(Clone, Copy, Debug, PartialEq, serde_enum_str::Deserialize_enum_str)]
207pub enum DeletionPolicy {
208 Delete,
209 Retain,
210 Snapshot,
211 RetainExceptOnCreate,
212}
213
214impl fmt::Display for DeletionPolicy {
215 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
216 match self {
217 Self::Delete => write!(f, "DELETE"),
218 Self::Retain => write!(f, "RETAIN"),
219 Self::Snapshot => write!(f, "SNAPSHOT"),
220 Self::RetainExceptOnCreate => write!(f, "RETAIN_EXCEPT_ON_CREATE"),
221 }
222 }
223}
224
225#[cfg(test)]
226mod tests;