derive_builder_core_fork_arti/
build_method.rs1use std::borrow::Cow;
2
3use doc_comment_from;
4use proc_macro2::{Span, TokenStream};
5use quote::{ToTokens, TokenStreamExt};
6use syn;
7use syn::spanned::Spanned;
8use BuilderPattern;
9use Initializer;
10use DEFAULT_STRUCT_NAME;
11
12use crate::DefaultExpression;
13
14#[derive(Debug)]
42pub struct BuildMethod<'a> {
43 pub enabled: bool,
45 pub ident: &'a syn::Ident,
47 pub visibility: Cow<'a, syn::Visibility>,
49 pub pattern: BuilderPattern,
51 pub target_ty: &'a syn::Ident,
55 pub target_ty_generics: Option<syn::TypeGenerics<'a>>,
57 pub error_ty: syn::Path,
59 pub initializers: Vec<TokenStream>,
61 pub doc_comment: Option<syn::Attribute>,
63 pub default_struct: Option<&'a DefaultExpression>,
67 pub validate_fn: Option<&'a syn::Path>,
70}
71
72impl<'a> ToTokens for BuildMethod<'a> {
73 fn to_tokens(&self, tokens: &mut TokenStream) {
74 let ident = &self.ident;
75 let vis = &self.visibility;
76 let target_ty = &self.target_ty;
77 let target_ty_generics = &self.target_ty_generics;
78 let initializers = &self.initializers;
79 let self_param = match self.pattern {
80 BuilderPattern::Owned => quote!(self),
81 BuilderPattern::Mutable | BuilderPattern::Immutable => quote!(&self),
82 };
83 let doc_comment = &self.doc_comment;
84 let default_struct = self.default_struct.as_ref().map(|default_expr| {
85 let ident = syn::Ident::new(DEFAULT_STRUCT_NAME, Span::call_site());
86 quote!(let #ident: #target_ty #target_ty_generics = #default_expr;)
87 });
88 let validate_fn = self
89 .validate_fn
90 .as_ref()
91 .map(|vfn| quote_spanned!(vfn.span() => #vfn(&self)?;));
92 let error_ty = &self.error_ty;
93
94 if self.enabled {
95 tokens.append_all(quote!(
96 #doc_comment
97 #vis fn #ident(#self_param)
98 -> ::derive_builder::export::core::result::Result<#target_ty #target_ty_generics, #error_ty>
99 {
100 #validate_fn
101 #default_struct
102 Ok(#target_ty {
103 #(#initializers)*
104 })
105 }
106 ))
107 }
108 }
109}
110
111impl<'a> BuildMethod<'a> {
112 pub fn doc_comment(&mut self, s: String) -> &mut Self {
114 self.doc_comment = Some(doc_comment_from(s));
115 self
116 }
117
118 pub fn push_initializer(&mut self, init: Initializer) -> &mut Self {
124 self.initializers.push(quote!(#init));
125 self
126 }
127}
128
129#[doc(hidden)]
137#[macro_export]
138macro_rules! default_build_method {
139 () => {
140 BuildMethod {
141 enabled: true,
142 ident: &syn::Ident::new("build", ::proc_macro2::Span::call_site()),
143 visibility: ::std::borrow::Cow::Owned(syn::parse_quote!(pub)),
144 pattern: BuilderPattern::Mutable,
145 target_ty: &syn::Ident::new("Foo", ::proc_macro2::Span::call_site()),
146 target_ty_generics: None,
147 error_ty: syn::parse_quote!(FooBuilderError),
148 initializers: vec![quote!(foo: self.foo,)],
149 doc_comment: None,
150 default_struct: None,
151 validate_fn: None,
152 }
153 };
154}
155
156#[cfg(test)]
157mod tests {
158 #[allow(unused_imports)]
159 use super::*;
160
161 #[test]
162 fn std() {
163 let build_method = default_build_method!();
164
165 #[rustfmt::skip]
166 assert_eq!(
167 quote!(#build_method).to_string(),
168 quote!(
169 pub fn build(&self) -> ::derive_builder::export::core::result::Result<Foo, FooBuilderError> {
170 Ok(Foo {
171 foo: self.foo,
172 })
173 }
174 )
175 .to_string()
176 );
177 }
178
179 #[test]
180 fn default_struct() {
181 let mut build_method = default_build_method!();
182 let alt_default =
183 DefaultExpression::explicit::<syn::Expr>(parse_quote!(Default::default()));
184 build_method.default_struct = Some(&alt_default);
185
186 #[rustfmt::skip]
187 assert_eq!(
188 quote!(#build_method).to_string(),
189 quote!(
190 pub fn build(&self) -> ::derive_builder::export::core::result::Result<Foo, FooBuilderError> {
191 let __default: Foo = { Default::default() };
192 Ok(Foo {
193 foo: self.foo,
194 })
195 }
196 )
197 .to_string()
198 );
199 }
200
201 #[test]
202 fn skip() {
203 let mut build_method = default_build_method!();
204 build_method.enabled = false;
205 build_method.enabled = false;
206
207 assert_eq!(quote!(#build_method).to_string(), quote!().to_string());
208 }
209
210 #[test]
211 fn rename() {
212 let ident = syn::Ident::new("finish", Span::call_site());
213 let mut build_method: BuildMethod = default_build_method!();
214 build_method.ident = &ident;
215
216 #[rustfmt::skip]
217 assert_eq!(
218 quote!(#build_method).to_string(),
219 quote!(
220 pub fn finish(&self) -> ::derive_builder::export::core::result::Result<Foo, FooBuilderError> {
221 Ok(Foo {
222 foo: self.foo,
223 })
224 }
225 )
226 .to_string()
227 );
228 }
229
230 #[test]
231 fn validation() {
232 let validate_path: syn::Path = parse_quote!(IpsumBuilder::validate);
233
234 let mut build_method: BuildMethod = default_build_method!();
235 build_method.validate_fn = Some(&validate_path);
236
237 #[rustfmt::skip]
238 assert_eq!(
239 quote!(#build_method).to_string(),
240 quote!(
241 pub fn build(&self) -> ::derive_builder::export::core::result::Result<Foo, FooBuilderError> {
242 IpsumBuilder::validate(&self)?;
243
244 Ok(Foo {
245 foo: self.foo,
246 })
247 }
248 )
249 .to_string()
250 );
251 }
252}