1 use quote::{ToTokens, TokenStreamExt}; 2 use proc_macro2::{Span, TokenStream}; 3 use syn; 4 use Bindings; 5 use Block; 6 use BuilderPattern; 7 use DEFAULT_STRUCT_NAME; 8 9 /// Initializer for the target struct fields, implementing `quote::ToTokens`. 10 /// 11 /// Lives in the body of `BuildMethod`. 12 /// 13 /// # Examples 14 /// 15 /// Will expand to something like the following (depending on settings): 16 /// 17 /// ```rust 18 /// # extern crate proc_macro2; 19 /// # #[macro_use] 20 /// # extern crate quote; 21 /// # extern crate syn; 22 /// # #[macro_use] 23 /// # extern crate derive_builder_core; 24 /// # use derive_builder_core::{DeprecationNotes, Initializer, BuilderPattern}; 25 /// # fn main() { 26 /// # let mut initializer = default_initializer!(); 27 /// # initializer.default_value = Some("42".parse().unwrap()); 28 /// # initializer.builder_pattern = BuilderPattern::Owned; 29 /// # 30 /// # assert_eq!(quote!(#initializer).to_string(), quote!( 31 /// foo: match self.foo { 32 /// Some(value) => value, 33 /// None => { 42 }, 34 /// }, 35 /// # ).to_string()); 36 /// # } 37 /// ``` 38 #[derive(Debug, Clone)] 39 pub struct Initializer<'a> { 40 /// Name of the target field. 41 pub field_ident: &'a syn::Ident, 42 /// Whether the builder implements a setter for this field. 43 pub setter_enabled: bool, 44 /// How the build method takes and returns `self` (e.g. mutably). 45 pub builder_pattern: BuilderPattern, 46 /// Default value for the target field. 47 /// 48 /// This takes precedence over a default struct identifier. 49 pub default_value: Option<Block>, 50 /// Whether the build_method defines a default struct. 51 pub use_default_struct: bool, 52 /// Bindings to libstd or libcore. 53 pub bindings: Bindings, 54 } 55 56 impl<'a> ToTokens for Initializer<'a> { to_tokens(&self, tokens: &mut TokenStream)57 fn to_tokens(&self, tokens: &mut TokenStream) { 58 trace!("Deriving initializer for `{}`.", self.field_ident); 59 60 let struct_field = &self.field_ident; 61 62 if self.setter_enabled { 63 let match_some = self.match_some(); 64 let match_none = self.match_none(); 65 let builder_field = &*struct_field; 66 tokens.append_all(quote!( 67 #struct_field: match self.#builder_field { 68 #match_some, 69 #match_none, 70 }, 71 )); 72 } else { 73 let default = self.default(); 74 tokens.append_all(quote!( 75 #struct_field: #default, 76 )); 77 } 78 } 79 } 80 81 impl<'a> Initializer<'a> { 82 /// To be used inside of `#struct_field: match self.#builder_field { ... }` match_some(&'a self) -> MatchSome83 fn match_some(&'a self) -> MatchSome { 84 match self.builder_pattern { 85 BuilderPattern::Owned => MatchSome::Move, 86 BuilderPattern::Mutable | BuilderPattern::Immutable => { 87 if self.bindings.no_std { 88 MatchSome::CloneNoStd 89 } else { 90 MatchSome::Clone 91 } 92 } 93 } 94 } 95 96 /// To be used inside of `#struct_field: match self.#builder_field { ... }` match_none(&'a self) -> MatchNone<'a>97 fn match_none(&'a self) -> MatchNone<'a> { 98 match self.default_value { 99 Some(ref expr) => MatchNone::DefaultTo(expr), 100 None => { 101 if self.use_default_struct { 102 MatchNone::UseDefaultStructField(self.field_ident) 103 } else if self.bindings.no_std { 104 MatchNone::ReturnErrorNoStd(format!( 105 "`{}` must be initialized", 106 self.field_ident 107 )) 108 } else { 109 MatchNone::ReturnError(format!("`{}` must be initialized", self.field_ident)) 110 } 111 } 112 } 113 } 114 default(&'a self) -> TokenStream115 fn default(&'a self) -> TokenStream { 116 match self.default_value { 117 Some(ref expr) => quote!(#expr), 118 None if self.use_default_struct => { 119 let struct_ident = syn::Ident::new(DEFAULT_STRUCT_NAME, Span::call_site()); 120 let field_ident = self.field_ident; 121 quote!(#struct_ident.#field_ident) 122 } 123 None => { 124 let default = self.bindings.default_trait(); 125 quote!(#default::default()) 126 } 127 } 128 } 129 } 130 131 /// To be used inside of `#struct_field: match self.#builder_field { ... }` 132 enum MatchNone<'a> { 133 /// Inner value must be a valid Rust expression 134 DefaultTo(&'a Block), 135 /// Inner value must be the field identifier 136 /// 137 /// The default struct must be in scope in the build_method. 138 UseDefaultStructField(&'a syn::Ident), 139 /// Inner value must be the field name 140 ReturnError(String), 141 /// Inner value must be the field name 142 ReturnErrorNoStd(String), 143 } 144 145 impl<'a> ToTokens for MatchNone<'a> { to_tokens(&self, tokens: &mut TokenStream)146 fn to_tokens(&self, tokens: &mut TokenStream) { 147 match *self { 148 MatchNone::DefaultTo(expr) => tokens.append_all(quote!( 149 None => #expr 150 )), 151 MatchNone::UseDefaultStructField(field_ident) => { 152 let struct_ident = syn::Ident::new(DEFAULT_STRUCT_NAME, Span::call_site()); 153 tokens.append_all(quote!( 154 None => #struct_ident.#field_ident 155 )) 156 } 157 MatchNone::ReturnError(ref err) => tokens.append_all(quote!( 158 None => return ::std::result::Result::Err(::std::string::String::from(#err)) 159 )), 160 MatchNone::ReturnErrorNoStd(ref err) => tokens.append_all(quote!( 161 None => return ::core::result::Result::Err( 162 ::alloc::string::String::from(#err)) 163 )), 164 } 165 } 166 } 167 168 /// To be used inside of `#struct_field: match self.#builder_field { ... }` 169 enum MatchSome { 170 Move, 171 Clone, 172 CloneNoStd, 173 } 174 175 impl<'a> ToTokens for MatchSome { to_tokens(&self, tokens: &mut TokenStream)176 fn to_tokens(&self, tokens: &mut TokenStream) { 177 match *self { 178 MatchSome::Move => tokens.append_all(quote!( 179 Some(value) => value 180 )), 181 MatchSome::Clone => tokens.append_all(quote!( 182 Some(ref value) => ::std::clone::Clone::clone(value) 183 )), 184 MatchSome::CloneNoStd => tokens.append_all(quote!( 185 Some(ref value) => ::core::clone::Clone::clone(value) 186 )), 187 } 188 } 189 } 190 191 /// Helper macro for unit tests. This is _only_ public in order to be accessible 192 /// from doc-tests too. 193 #[doc(hidden)] 194 #[macro_export] 195 macro_rules! default_initializer { 196 () => { 197 Initializer { 198 field_ident: &syn::Ident::new("foo", ::proc_macro2::Span::call_site()), 199 setter_enabled: true, 200 builder_pattern: BuilderPattern::Mutable, 201 default_value: None, 202 use_default_struct: false, 203 bindings: Default::default(), 204 } 205 }; 206 } 207 208 #[cfg(test)] 209 mod tests { 210 #[allow(unused_imports)] 211 use super::*; 212 213 #[test] immutable()214 fn immutable() { 215 let mut initializer = default_initializer!(); 216 initializer.builder_pattern = BuilderPattern::Immutable; 217 218 assert_eq!( 219 quote!(#initializer).to_string(), 220 quote!( 221 foo: match self.foo { 222 Some(ref value) => ::std::clone::Clone::clone(value), 223 None => return ::std::result::Result::Err(::std::string::String::from( 224 "`foo` must be initialized" 225 )), 226 }, 227 ).to_string() 228 ); 229 } 230 231 #[test] mutable()232 fn mutable() { 233 let mut initializer = default_initializer!(); 234 initializer.builder_pattern = BuilderPattern::Mutable; 235 236 assert_eq!( 237 quote!(#initializer).to_string(), 238 quote!( 239 foo: match self.foo { 240 Some(ref value) => ::std::clone::Clone::clone(value), 241 None => return ::std::result::Result::Err(::std::string::String::from( 242 "`foo` must be initialized" 243 )), 244 }, 245 ).to_string() 246 ); 247 } 248 249 #[test] owned()250 fn owned() { 251 let mut initializer = default_initializer!(); 252 initializer.builder_pattern = BuilderPattern::Owned; 253 254 assert_eq!( 255 quote!(#initializer).to_string(), 256 quote!( 257 foo: match self.foo { 258 Some(value) => value, 259 None => return ::std::result::Result::Err(::std::string::String::from( 260 "`foo` must be initialized" 261 )), 262 }, 263 ).to_string() 264 ); 265 } 266 267 #[test] default_value()268 fn default_value() { 269 let mut initializer = default_initializer!(); 270 initializer.default_value = Some("42".parse().unwrap()); 271 272 assert_eq!( 273 quote!(#initializer).to_string(), 274 quote!( 275 foo: match self.foo { 276 Some(ref value) => ::std::clone::Clone::clone(value), 277 None => { 42 }, 278 }, 279 ).to_string() 280 ); 281 } 282 283 #[test] default_struct()284 fn default_struct() { 285 let mut initializer = default_initializer!(); 286 initializer.use_default_struct = true; 287 288 assert_eq!( 289 quote!(#initializer).to_string(), 290 quote!( 291 foo: match self.foo { 292 Some(ref value) => ::std::clone::Clone::clone(value), 293 None => __default.foo, 294 }, 295 ).to_string() 296 ); 297 } 298 299 #[test] setter_disabled()300 fn setter_disabled() { 301 let mut initializer = default_initializer!(); 302 initializer.setter_enabled = false; 303 304 assert_eq!( 305 quote!(#initializer).to_string(), 306 quote!(foo: ::std::default::Default::default(),).to_string() 307 ); 308 } 309 310 #[test] no_std()311 fn no_std() { 312 let mut initializer = default_initializer!(); 313 initializer.bindings.no_std = true; 314 315 assert_eq!( 316 quote!(#initializer).to_string(), 317 quote!( 318 foo: match self.foo { 319 Some(ref value) => ::core::clone::Clone::clone(value), 320 None => return ::core::result::Result::Err(::alloc::string::String::from( 321 "`foo` must be initialized" 322 )), 323 }, 324 ).to_string() 325 ); 326 } 327 328 #[test] no_std_setter_disabled()329 fn no_std_setter_disabled() { 330 let mut initializer = default_initializer!(); 331 initializer.bindings.no_std = true; 332 initializer.setter_enabled = false; 333 334 assert_eq!( 335 quote!(#initializer).to_string(), 336 quote!(foo: ::core::default::Default::default(),).to_string() 337 ); 338 } 339 } 340