1use proc_macro2::TokenStream;
2use syn::{punctuated::Punctuated, token::Comma, DeriveInput, Field};
3
4pub struct BuilderBuilder {
5 ast: DeriveInput,
7 should_default: bool,
9}
10
11impl BuilderBuilder {
12 pub fn new(ast: DeriveInput) -> Self {
13 let should_default = ast
14 .attrs
15 .iter()
16 .filter(|attr| attr.path().is_ident("builder"))
17 .any(|attr| {
18 if let Ok(value) = attr.parse_args::<TokenStream>() {
19 value.to_string() == "default"
20 } else {
21 false
22 }
23 });
24
25 Self {
26 ast,
27 should_default,
28 }
29 }
30
31 fn ident(&self) -> &syn::Ident {
32 &self.ast.ident
33 }
34
35 fn builder_name(&self) -> syn::Ident {
36 quote::format_ident!("{}Builder", self.ident())
37 }
38
39 fn builder_fields(&self) -> Vec<TokenStream> {
40 self.target_fields()
41 .iter()
42 .map(|field| {
43 let field_name = field.ident.as_ref().expect("Expected field name");
44 let field_type = &field.ty;
45 quote::quote!(
46 #field_name: Option::<#field_type>,
47 )
48 })
49 .collect::<Vec<_>>()
50 }
51
52 fn builder_field_values(&self) -> Vec<TokenStream> {
53 match self.should_default {
54 true => self
55 .target_fields()
56 .iter()
57 .map(|field| {
58 let field_name = field.ident.as_ref().expect("Expected field name");
59 quote::quote!(
60 #field_name: Some(Default::default()),
61 )
62 })
63 .collect(),
64 false => self
65 .target_fields()
66 .iter()
67 .map(|field| {
68 let field_name = field.ident.as_ref().expect("Expected field name");
69 quote::quote!(
70 #field_name: None,
71 )
72 })
73 .collect(),
74 }
75 }
76
77 fn builder_methods(&self) -> Vec<TokenStream> {
78 self.target_fields()
79 .iter()
80 .map(|field| {
81 let field_name = field.ident.as_ref().expect("Expected field name");
82 let field_type = &field.ty;
83 quote::quote!(
84 fn #field_name(&mut self, value: #field_type) -> &mut Self {
85 self.#field_name = Some(value);
86 self
87 }
88 )
89 })
90 .collect()
91 }
92
93 fn builder_build_checks(&self) -> Vec<TokenStream> {
94 self.target_fields().iter().map(|field| {
95 let field_name = field.ident.as_ref().expect("Expected field name");
96 quote::quote!(
97 if self.#field_name.is_none() {
98 return Err(format!("Expected field to be set: {}", stringify!(#field_name).to_string()));
99 }
100 )
101 }).collect::<Vec<_>>()
102 }
103
104 fn target_fields(&self) -> &Punctuated<Field, Comma> {
105 match self.ast.data {
106 syn::Data::Struct(syn::DataStruct {
107 fields: syn::Fields::Named(syn::FieldsNamed { ref named, .. }),
108 ..
109 }) => named,
110 _ => panic!("Builder macro only supports structs with named fields"),
111 }
112 }
113
114 fn target_field_values(&self) -> Vec<TokenStream> {
115 self.target_fields().iter().map(|field| {
116 let field_name = field.ident.as_ref().expect("Expected field name");
117 quote::quote!(
118 #field_name: self.#field_name.clone().expect(format!("Expected field to be set: {}", stringify!(#field_name).to_string()).as_str()),
119 )
120 }).collect()
121 }
122
123 pub fn build_builder(&self) -> TokenStream {
124 let ident = &self.ast.ident;
125
126 let builder_struct_name = self.builder_name();
128 let builder_fields = self.builder_fields();
129 let builder_field_values = self.builder_field_values();
130 let builder_methods = self.builder_methods();
131 let instance_field_values = self.target_field_values();
132 let checks_for_builder = self.builder_build_checks();
133
134 quote::quote!(
135 #[derive(Clone, Debug)]
136 struct #builder_struct_name {
137 #(#builder_fields)*
138 }
139
140 #[allow(dead_code)]
141 impl #builder_struct_name {
142 fn new() -> #builder_struct_name {
143 #builder_struct_name {
144 #(#builder_field_values)*
145 }
146 }
147
148 #(#builder_methods)*
149
150 fn build(&self) -> Result<#ident, String> {
151 #(#checks_for_builder)*
152 Ok(#ident {
153 #(#instance_field_values)*
154 })
155 }
156 }
157
158 )
159 }
160}