tf_bindgen/codegen/
field_info.rs1use itertools::Itertools;
2
3use super::{
4 path::Path,
5 type_info::{TypeInfo, Wrapper},
6};
7
8#[derive(derive_builder::Builder, Clone, Debug)]
9pub struct FieldInfo {
10 path: Path,
11 name: String,
12 type_info: TypeInfo,
13 description: Option<String>,
14 optional: bool,
15 computed: bool,
16}
17
18impl FieldInfo {
19 pub fn builder() -> FieldInfoBuilder {
20 FieldInfoBuilder::default()
21 }
22
23 pub fn gen_field(&self) -> String {
24 let name = self.name();
25 if self.is_computed() && !self.is_optional() {
26 let type_name = self.type_info.source();
27 return format!(
28 "#[serde(skip_serializing)] pub {name}: ::tf_bindgen::value::Cell<::tf_bindgen::value::Computed<{type_name}>>"
29 );
30 }
31 let type_name = self.field_type();
32 format!("pub {name}: {type_name}")
33 }
34
35 pub fn gen_builder_field(&self) -> String {
36 let name = self.name();
37 let type_name = self.type_info.source();
38 format!("{name}: ::std::option::Option<{type_name}>")
39 }
40
41 pub fn name(&self) -> &str {
42 fix_ident(&self.name)
43 }
44
45 pub fn raw_name(&self) -> &str {
46 &self.name
47 }
48
49 pub fn is_optional(&self) -> bool {
51 self.optional
52 }
53
54 pub fn is_computed(&self) -> bool {
56 self.computed
57 }
58
59 pub fn path_ref(&self) -> String {
61 self.path
62 .segments()
63 .chain(Some(&self.name).into_iter())
64 .join(".")
65 }
66
67 fn ty(&self) -> String {
68 let type_name = self.type_info.source();
69 if self.is_optional() {
70 format!("::std::option::Option<{type_name}>")
71 } else {
72 type_name
73 }
74 }
75
76 pub fn field_type(&self) -> String {
79 let type_name = self.ty();
80 format!("::tf_bindgen::value::Cell<{type_name}>")
81 }
82
83 pub fn builder_setter_impl(&self) -> String {
85 let name = self.name();
86 let fn_name = match name {
87 "build" => "build_",
88 _ => name,
89 };
90 let type_name = self.type_info.type_name();
91 let convert = match self.type_info.wrapper() {
92 Wrapper::List => "into_value_list()",
93 Wrapper::Map => "into_value_map()",
94 Wrapper::Type => "into_value()",
95 Wrapper::Set => "into_value_set()",
96 };
97 let impl_type = match self.type_info.wrapper() {
98 Wrapper::List => "IntoValueList",
99 Wrapper::Map => "IntoValueMap",
100 Wrapper::Type => "IntoValue",
101 Wrapper::Set => "IntoValueSet",
102 };
103 let body_impl = match self.type_info.wrapper() {
104 Wrapper::List => format!(
105 r#"let new_list = value.{convert};
106 if let Some(list) = &mut self.{name} {{
107 list.extend(new_list);
108 }} else {{
109 self.{name} = Some(new_list);
110 }}
111 self"#
112 ),
113 _ => format!(r#"self.{name} = Some(value.{convert}); self"#),
114 };
115 format!(
116 r#"pub fn {fn_name}(&mut self, value: impl ::tf_bindgen::value::{impl_type}<{type_name}>) -> &mut Self {{
117 {body_impl}
118 }}"#
119 )
120 }
121
122 pub fn doc_str(&self) -> String {
124 self.description
125 .iter()
126 .flat_map(|desc| desc.split('\n'))
127 .map(|desc| "///".to_owned() + desc)
128 .join("\n")
129 }
130}
131
132fn fix_ident(input: &str) -> &str {
134 assert!(!input.is_empty(), "ident: '{input}' is empty");
135 match input {
136 "type" => "r#type",
137 "as" => "r#as",
138 "async" => "r#async",
139 "await" => "r#await",
140 "box" => "r#box",
141 "break" => "r#break",
142 "const" => "r#const",
143 "continue" => "r#continue",
144 "dyn" => "r#dyn",
145 "else" => "r#else",
146 "enum" => "r#enum",
147 "extern" => "r#extern",
148 "fn" => "r#final",
149 "for" => "r#for",
150 "if" => "r#if",
151 "impl" => "r#impl",
152 "in" => "r#in",
153 "let" => "r#let",
154 "loop" => "r#loop",
155 "macro" => "r#macro",
156 "match" => "r#match",
157 "mod" => "r#mod",
158 "move" => "r#move",
159 "mut" => "r#mut",
160 "pub" => "r#pub",
161 "ref" => "r#ref",
162 "return" => "r#return",
163 "self" => "r#self",
164 "static" => "r#static",
165 "super" => "r#super",
166 "trait" => "r#trait",
167 "union" => "r#union",
168 "unsafe" => "r#unsafe",
169 "use" => "r#use",
170 "where" => "r#where",
171 "while" => "r#while",
172 "yield" => "r#yield",
173 _ => input,
174 }
175}