chromiumoxide_pdl/build/
builder.rs1use proc_macro2::{Ident, TokenStream};
2use quote::{format_ident, quote};
3
4use crate::build::generator::generate_field_name;
5use crate::build::types::FieldDefinition;
6use heck::*;
7pub struct Builder {
8    pub fields: Vec<(TokenStream, FieldDefinition)>,
9    pub name: Ident,
10}
11
12impl Builder {
13    pub fn new(name: Ident) -> Self {
14        Self {
15            name,
16            fields: vec![],
17        }
18    }
19
20    pub fn has_mandatory_types(&self) -> bool {
21        self.mandatory().any(|f| !f.optional)
22    }
23
24    fn mandatory(&self) -> impl Iterator<Item = &FieldDefinition> + '_ {
25        self.fields
26            .iter()
27            .filter(|(_, f)| !f.optional)
28            .map(|(_, f)| f)
29    }
30
31    pub fn generate_struct_def(&self) -> TokenStream {
32        let name = &self.name;
33        let field_definitions = self.fields.iter().map(|(def, _)| def);
34        quote! {
35             pub struct #name {
36                #(#field_definitions),*
37             }
38        }
39    }
40
41    pub fn generate_impl(&self) -> TokenStream {
42        let mut stream = TokenStream::default();
43        if self.fields.is_empty() {
44            return stream;
45        }
46
47        let name = &self.name;
48
49        let optionals = self
50            .fields
51            .iter()
52            .filter(|(_, f)| f.optional)
53            .map(|(_, f)| &f.name_ident);
54
55        let mut mandatory_param_name = vec![];
56        let mut mandatory_param_ty = vec![];
57        let mut assign = vec![];
58
59        for field in self.mandatory() {
60            let field_name = &field.name_ident;
61            mandatory_param_name.push(field_name);
62            mandatory_param_ty.push(field.ty.param_type_def());
63            if field.ty.is_vec {
64                assign.push(quote! {#field_name});
65            } else if field.ty.needs_box {
66                assign.push(quote! {#field_name : Box::new(#field_name.into())});
67            } else {
68                assign.push(quote! {#field_name : #field_name.into()});
69            }
70        }
71
72        if !mandatory_param_name.is_empty() && mandatory_param_name.len() <= 4 {
75            stream.extend(quote! {
76                impl #name {
77                    pub fn new(#(#mandatory_param_name : #mandatory_param_ty),*) -> Self {
78                        Self {
79                          #(#assign,)*
80                          #(#optionals : None),*
81                        }
82                    }
83                }
84            })
85        }
86
87        if mandatory_param_name.len() == 1 {
90            let f = self.mandatory().next().unwrap();
91            if !f.ty.is_vec && f.ty.ty.to_string() == "String" {
92                stream.extend(quote! {
93                    impl<T: Into<String>> From<T> for #name {
94                          fn from(url: T) -> Self {
95                              #name::new(url)
96                          }
97                    }
98                });
99            }
100        }
101
102        let builder = format_ident!("{}Builder", self.name);
110
111        let mut setters = TokenStream::default();
112        let mut names = vec![];
113        let mut builder_type_defs = TokenStream::default();
114        let mut build_fn_assigns = TokenStream::default();
115
116        for field in self.fields.iter().map(|(_, f)| f) {
117            let field_name = &field.name_ident;
118            names.push(field_name);
119            let builder_ty = field.ty.builder_type();
120            builder_type_defs.extend(quote! {
121                #field_name: Option<#builder_ty>,
122            });
123
124            let ty_param = field.ty.param_type_def();
125            let assign = if field.ty.is_vec {
126                quote! {#field_name}
127            } else {
128                quote! {#field_name.into()}
129            };
130
131            if field.ty.is_vec {
132                let ty = &field.ty.ty;
133                let s = field_name.to_string().to_snake_case();
134                let (iter_setter_name, single_setter_name) = if s.ends_with('s') {
135                    (
136                        field_name.clone(),
137                        format_ident!("{}", generate_field_name(&s[..s.len() - 1])),
138                    )
139                } else {
140                    (format_ident!("{}s", s), field_name.clone())
141                };
142
143                setters.extend(
145                  quote! {
146                     pub fn #single_setter_name(mut self, #single_setter_name: impl Into<#ty>) -> Self {
147                        let v = self.#field_name.get_or_insert(Vec::new());
148                        v.push(#single_setter_name.into());
149                        self
150                     }
151
152                    pub fn #iter_setter_name<I, S>(mut self, #iter_setter_name: I) -> Self
153                    where
154                        I: IntoIterator<Item = S>,
155                        S: Into<#ty>,
156                    {
157                        let v = self.#field_name.get_or_insert(Vec::new());
158                        for val in #iter_setter_name {
159                            v.push(val.into());
160                        }
161                        self
162                    }
163                  }
164                );
165            } else {
166                setters.extend(quote! {
167                    pub fn #field_name(mut self, #field_name : #ty_param ) -> Self {
168                        self.#field_name = Some(#assign);
169                        self
170                    }
171                });
172            }
173
174            if field.optional {
176                if field.ty.needs_box {
177                    build_fn_assigns.extend(quote! {
178                        #field_name : self.#field_name.map(Box::new),
179                    })
180                } else {
181                    build_fn_assigns.extend(quote! {
182                        #field_name : self.#field_name,
183                    })
184                }
185            } else if field.ty.needs_box {
186                build_fn_assigns.extend(
187                        quote!{
188                            #field_name : Box::new(self.#field_name.ok_or_else(||std::stringify!("Field `{}` is mandatory.", std::stringify!(#field_name))))?,
189                        }
190                    )
191            } else {
192                build_fn_assigns.extend(
193                        quote!{
194                            #field_name : self.#field_name.ok_or_else(||format!("Field `{}` is mandatory.", std::stringify!(#field_name)))?,
195                        }
196                    )
197            }
198        }
199
200        let build_fn = if mandatory_param_name.is_empty() {
201            quote! {
202                pub fn build(self) -> #name {
203                    #name {
204                        #build_fn_assigns
205                    }
206                }
207            }
208        } else {
209            quote! {
210                pub fn build(self) -> Result<#name, String> {
211                    Ok(#name {
212                        #build_fn_assigns
213                    })
214                }
215            }
216        };
217
218        stream.extend(quote! {
219               impl #name {
220                    pub fn builder() -> #builder {
221                        #builder::default()
222                    }
223               }
224
225               #[derive(Default, Clone)]
226               pub struct #builder {
227                    #builder_type_defs
228               }
229
230               impl #builder {
231                    #setters
232                    #build_fn
233               }
234        });
235
236        stream
237    }
238}