tf_bindgen/codegen/
struct_info.rs1use 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 .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}