tf_bindgen/codegen/
struct_info.rs

1use heck::ToUpperCamelCase;
2use itertools::Itertools;
3
4use super::field_info::FieldInfo;
5use super::path::Path;
6
7#[derive(Clone, Debug)]
8pub enum StructType {
9    Provider {
10        ty: String,
11        ver: String,
12        nested: Vec<StructInfo>,
13    },
14    Construct {
15        ty: String,
16        nested: Vec<StructInfo>,
17    },
18    Nested,
19}
20
21#[derive(derive_builder::Builder, Clone, Debug)]
22pub struct StructInfo {
23    ty: StructType,
24    path: Path,
25    name: String,
26    fields: Vec<FieldInfo>,
27}
28
29impl StructInfo {
30    pub fn builder() -> StructInfoBuilder {
31        StructInfoBuilder::default()
32    }
33
34    pub fn name(&self) -> &str {
35        &self.name
36    }
37
38    pub fn ty(&self) -> &str {
39        match &self.ty {
40            StructType::Provider { ty, .. } => ty,
41            StructType::Construct { ty, .. } => ty,
42            StructType::Nested => unimplemented!(),
43        }
44    }
45
46    pub fn gen_rust(&self) -> String {
47        let mut sources = vec![
48            self.gen_struct(),
49            self.gen_struct_impl(),
50            self.gen_builder_struct(),
51            self.gen_build_impl(),
52            self.gen_builder_setter(),
53        ];
54        match &self.ty {
55            StructType::Provider { nested, .. } | StructType::Construct { nested, .. } => {
56                sources.extend(nested.iter().map(|nested| nested.gen_rust()))
57            }
58            StructType::Nested => {}
59        }
60        sources.join("\n")
61    }
62
63    pub fn gen_struct(&self) -> String {
64        let prefix = self.path.type_name();
65        let name = self.name.to_upper_camel_case();
66        let fields = self.fields.iter().map(FieldInfo::gen_field).join(",\n");
67        match self.ty {
68            StructType::Provider { .. } => format!(
69                r#"#[derive(::std::clone::Clone, ::tf_bindgen::serde::Serialize)]
70				#[serde(crate = "::tf_bindgen::serde")]
71				pub struct {prefix}{name} {{
72					#[serde(skip_serializing)]
73					__m_scope: ::std::rc::Rc<dyn ::tf_bindgen::Scope>, 
74					{fields}
75				}}"#
76            ),
77            StructType::Construct { .. } => format!(
78                r#"#[derive(::std::clone::Clone, ::tf_bindgen::serde::Serialize)]
79				#[serde(crate = "::tf_bindgen::serde")]
80				pub struct {prefix}{name} {{
81					#[serde(skip_serializing)]
82					__m_scope: ::std::rc::Rc<dyn ::tf_bindgen::Scope>, 
83					#[serde(skip_serializing)]
84					__m_name: ::std::string::String,
85					{fields}
86				}}"#
87            ),
88            StructType::Nested => format!(
89                r#"#[derive(::std::clone::Clone, ::tf_bindgen::serde::Serialize)]
90				#[serde(crate = "::tf_bindgen::serde")]
91				pub struct {prefix}{name} {{
92					{fields}
93				}}"#
94            ),
95        }
96    }
97
98    pub fn gen_builder_struct(&self) -> String {
99        let prefix = self.path.type_name();
100        let name = self.name.to_upper_camel_case();
101        let fields = self
102            .fields
103            .iter()
104            .filter(|field| !field.is_computed() || field.is_optional())
105            .map(FieldInfo::gen_builder_field)
106            .join(",\n");
107        match self.ty {
108            StructType::Provider { .. } => format!(
109                r#"pub struct {prefix}{name}Builder {{
110					__m_scope: ::std::rc::Rc<dyn ::tf_bindgen::Scope>, 
111					{fields}
112				}}"#
113            ),
114            StructType::Construct { .. } => format!(
115                r#"pub struct {prefix}{name}Builder {{
116					__m_scope: ::std::rc::Rc<dyn ::tf_bindgen::Scope>, 
117					__m_name: ::std::string::String,
118					{fields}
119				}}"#
120            ),
121            StructType::Nested => format!(
122                r#"pub struct {prefix}{name}Builder {{
123					{fields}
124				}}"#
125            ),
126        }
127    }
128
129    pub fn gen_struct_impl(&self) -> String {
130        let prefix = self.path.type_name();
131        let name = self.name.to_upper_camel_case();
132        let fields = self
133            .fields
134            .iter()
135            .filter(|field| !field.is_computed() || field.is_optional())
136            .map(|field| {
137                let name = field.name();
138                format!("{name}: None")
139            })
140            .join(",\n");
141        let prepare_fields = self
142            .fields
143            .iter()
144            .map(|field| {
145                let name = field.name();
146                format!(
147                    r#"{name}: {{
148						let path = format!("{{prefix}}.{name}");
149						self.{name}.prepare(path)
150					}}"#
151                )
152            })
153            .join(",\n");
154        match &self.ty {
155            StructType::Provider { ty, .. } => format!(
156                r#"impl {prefix}{name} {{
157					pub fn create<C: ::tf_bindgen::Scope + 'static>(
158						scope: &::std::rc::Rc<C>,
159					) -> {prefix}{name}Builder {{
160						{prefix}{name}Builder {{
161							__m_scope: scope.clone(),
162							{fields}
163						}}
164					}}
165
166				}}
167
168				impl ::tf_bindgen::Scope for {prefix}{name} {{
169					fn stack(&self) -> ::tf_bindgen::Stack {{
170						self.__m_scope.stack()
171					}}
172
173					fn path(&self) -> ::tf_bindgen::Path {{
174						let mut path = self.__m_scope.path();
175						path.push("{ty}");
176						path
177					}}
178				}}"#
179            ),
180            StructType::Construct { .. } => format!(
181                r#"impl {prefix}{name} {{
182					pub fn create<C: ::tf_bindgen::Scope + 'static>(
183						scope: &::std::rc::Rc<C>,
184						name: impl ::std::convert::Into<::std::string::String>
185					) -> {prefix}{name}Builder {{
186						{prefix}{name}Builder {{
187							__m_scope: scope.clone(),
188							__m_name: name.into(),
189							{fields}
190						}}
191					}}
192				}}
193
194				impl ::tf_bindgen::Scope for {prefix}{name} {{
195					fn stack(&self) -> ::tf_bindgen::Stack {{
196						self.__m_scope.stack()
197					}}
198
199					fn path(&self) -> ::tf_bindgen::Path {{
200						let mut path = self.__m_scope.path();
201						path.push(&self.__m_name);
202						path
203					}}
204				}}
205 
206				impl ::tf_bindgen::value::Prepare for {prefix}{name} {{
207					fn prepare(self, prefix: impl Into<::std::string::String>) -> Self {{
208						let prefix = prefix.into();
209						Self {{
210							__m_scope: self.__m_scope,
211							__m_name: self.__m_name,
212							{prepare_fields}
213						}}
214					}}
215				}}"#
216            ),
217            StructType::Nested => format!(
218                r#"impl {prefix}{name} {{
219					pub fn builder() -> {prefix}{name}Builder {{
220						{prefix}{name}Builder {{
221							{fields}
222						}}
223					}}
224				}}
225
226				impl ::tf_bindgen::value::Prepare for {prefix}{name} {{
227					fn prepare(self, prefix: impl Into<::std::string::String>) -> Self {{
228						let prefix = prefix.into();
229						Self {{
230							{prepare_fields}
231						}}
232					}}
233				}}
234
235				impl ::tf_bindgen::value::IntoValue<{prefix}{name}> for {prefix}{name} {{
236					fn into_value(self) -> ::tf_bindgen::Value<{prefix}{name}> {{
237						::tf_bindgen::Value::Value {{ value: ::std::rc::Rc::new(self) }}
238					}}
239				}}
240
241				impl ::tf_bindgen::value::IntoValueList<{prefix}{name}> for {prefix}{name} {{
242					fn into_value_list(self) -> ::std::vec::Vec<::tf_bindgen::Value<{prefix}{name}>> {{
243						use ::tf_bindgen::value::IntoValue;
244						std::vec![self.into_value()]
245					}}
246				}}"#
247            ),
248        }
249    }
250
251    pub fn gen_builder_setter(&self) -> String {
252        let prefix = self.path.type_name();
253        let name = self.name.to_upper_camel_case();
254        let setter = self
255            .fields
256            .iter()
257            .filter(|field| !field.is_computed() || field.is_optional())
258            .map(FieldInfo::builder_setter_impl)
259            .join("\n");
260        format!(
261            r#"impl {prefix}{name}Builder {{
262				{setter}
263			}}"#
264        )
265    }
266
267    pub fn gen_build_impl(&self) -> String {
268        let prefix = self.path.type_name();
269        let name = self.name.to_upper_camel_case();
270        let assign = self
271            .fields
272            .iter()
273            // .filter(|field| !field.is_computed() || field.is_optional())
274            .map(|field| {
275                let name = field.name();
276                if field.is_optional() {
277                    format!(r#"{name}: ::tf_bindgen::value::Cell::new("{name}", self.{name}.clone())"#)
278                } else if field.is_computed() {
279					format!(r#"{name}: ::tf_bindgen::value::Cell::new("{name}", tf_bindgen::value::Computed::default())"#)
280				} else {
281                    format!(r#"{name}: ::tf_bindgen::value::Cell::new("{name}", self.{name}.clone().expect("field `{name}`"))"#)
282                }
283            })
284            .join(",\n");
285        let config: String = self
286            .fields
287            .iter()
288            .filter(|field| !field.is_computed() || field.is_optional())
289            .map(|field| {
290                let name = field.name();
291                let raw_name = field.raw_name();
292                format!(
293                    r#"
294						let value = ::tf_bindgen::json::to_value(&self.{name}).unwrap();
295						config.insert("{raw_name}".to_string(), value);
296					"#
297                )
298            })
299            .collect();
300        match &self.ty {
301            StructType::Provider { ver, .. } => format!(
302                r#"impl ::tf_bindgen::Provider for {prefix}{name} {{
303					fn to_schema(&self) -> (::std::string::String, ::tf_bindgen::schema::document::Provider) {{
304						let mut config = ::tf_bindgen::schema::document::Provider::new();
305						{config}
306						("{ver}".to_string(), config)
307					}}
308				}}
309
310				impl {prefix}{name}Builder {{
311					pub fn build(&mut self) -> ::std::rc::Rc<{prefix}{name}> {{
312						use tf_bindgen::Scope;
313						let this = ::std::rc::Rc::new({prefix}{name} {{
314							__m_scope: self.__m_scope.clone(),
315							{assign}
316						}});
317						this.stack().add_provider(this.clone());
318						this
319					}}
320				}}"#
321            ),
322            StructType::Construct { ty, .. } => format!(
323                r#"impl ::tf_bindgen::L1Construct for {prefix}{name} {{
324					fn to_schema(&self) -> (::std::string::String, ::tf_bindgen::schema::document::Resource) {{
325						use tf_bindgen::Scope;
326						let mut config = ::std::collections::HashMap::new();
327						{config}
328						let path = self.path();
329						let resource = ::tf_bindgen::schema::document::Resource {{
330							meta: ::tf_bindgen::schema::document::ResourceMeta {{
331								metadata: ::tf_bindgen::schema::document::ResourceMetadata {{
332									path: path.to_string(),
333									unique_id: path.name().to_string(),
334								}},
335							}},
336							config
337						}};
338						("{ty}".to_string(), resource)
339					}}
340				}}
341
342
343				impl {prefix}{name}Builder {{
344					pub fn build(&mut self) -> ::std::rc::Rc<{prefix}{name}> {{
345						use tf_bindgen::Scope;
346						use ::tf_bindgen::value::Prepare;
347						let this = {prefix}{name} {{
348							__m_scope: self.__m_scope.clone(),
349							__m_name: self.__m_name.clone(),
350							{assign}
351						}};
352						let path = format!("{ty}.{{}}", this.path().id());
353						let this = ::std::rc::Rc::new(this.prepare(path));
354						this.stack().add_resource(this.clone());
355						this
356					}}
357				}}"#
358            ),
359            StructType::Nested => format!(
360                r#"impl {prefix}{name}Builder {{
361					pub fn build(&mut self) -> {prefix}{name} {{
362						{prefix}{name} {{
363							{assign}
364						}}
365					}}
366				}}"#
367            ),
368        }
369    }
370}