cdk_from_cfn/parser/resource/
mod.rs

1use 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;