| 1 | use std::borrow::Cow; |
| 2 | |
| 3 | use proc_macro2::TokenStream; |
| 4 | use quote::{ToTokens, TokenStreamExt}; |
| 5 | |
| 6 | /// Field for the builder struct, implementing `quote::ToTokens`. |
| 7 | /// |
| 8 | /// # Examples |
| 9 | /// |
| 10 | /// Will expand to something like the following (depending on settings): |
| 11 | /// |
| 12 | /// ```rust,ignore |
| 13 | /// # extern crate proc_macro2; |
| 14 | /// # #[macro_use ] |
| 15 | /// # extern crate quote; |
| 16 | /// # #[macro_use ] |
| 17 | /// # extern crate syn; |
| 18 | /// # #[macro_use ] |
| 19 | /// # extern crate derive_builder_core; |
| 20 | /// # use derive_builder_core::{BuilderField, BuilderPattern}; |
| 21 | /// # fn main() { |
| 22 | /// # let attrs = vec![parse_quote!(#[some_attr])]; |
| 23 | /// # let mut field = default_builder_field!(); |
| 24 | /// # field.attrs = attrs.as_slice(); |
| 25 | /// # |
| 26 | /// # assert_eq!(quote!(#field).to_string(), quote!( |
| 27 | /// #[some_attr] pub foo: ::derive_builder::export::core::option::Option<String>, |
| 28 | /// # ).to_string()); |
| 29 | /// # } |
| 30 | /// ``` |
| 31 | #[derive (Debug, Clone)] |
| 32 | pub struct BuilderField<'a> { |
| 33 | /// Path to the root of the derive_builder crate. |
| 34 | pub crate_root: &'a syn::Path, |
| 35 | /// Name of the target field. |
| 36 | pub field_ident: &'a syn::Ident, |
| 37 | /// Type of the builder field. |
| 38 | pub field_type: BuilderFieldType<'a>, |
| 39 | /// Visibility of this builder field, e.g. `syn::Visibility::Public`. |
| 40 | pub field_visibility: Cow<'a, syn::Visibility>, |
| 41 | /// Attributes which will be attached to this builder field. |
| 42 | pub attrs: &'a [syn::Attribute], |
| 43 | } |
| 44 | |
| 45 | impl<'a> ToTokens for BuilderField<'a> { |
| 46 | fn to_tokens(&self, tokens: &mut TokenStream) { |
| 47 | let ident: &'a Ident = self.field_ident; |
| 48 | let vis: &Cow<'a, Visibility> = &self.field_visibility; |
| 49 | let ty: &BuilderFieldTypeWithCrateRoot<'_> = &self.field_type.with_crate_root(self.crate_root); |
| 50 | let attrs: &'a [Attribute] = self.attrs; |
| 51 | tokens.append_all(iter:quote!( |
| 52 | #(#attrs)* #vis #ident: #ty, |
| 53 | )); |
| 54 | } |
| 55 | } |
| 56 | |
| 57 | impl<'a> BuilderField<'a> { |
| 58 | /// Emits a struct field initializer that initializes the field to `Default::default`. |
| 59 | pub fn default_initializer_tokens(&self) -> TokenStream { |
| 60 | let ident: &'a Ident = self.field_ident; |
| 61 | let crate_root: &'a Path = self.crate_root; |
| 62 | quote! { #ident : #crate_root::export::core::default::Default::default(), } |
| 63 | } |
| 64 | } |
| 65 | |
| 66 | /// The type of a field in the builder struct |
| 67 | #[derive (Debug, Clone)] |
| 68 | pub enum BuilderFieldType<'a> { |
| 69 | /// The corresonding builder field will be `Option<field_type>`. |
| 70 | Optional(&'a syn::Type), |
| 71 | /// The corresponding builder field will be just this type |
| 72 | Precise(&'a syn::Type), |
| 73 | /// The corresponding builder field will be a PhantomData |
| 74 | /// |
| 75 | /// We do this if if the field is disabled. We mustn't just completely omit the field from the builder: |
| 76 | /// if we did that, the builder might have unused generic parameters (since we copy the generics from |
| 77 | /// the target struct). Using a PhantomData of the original field type provides the right generic usage |
| 78 | /// (and the right variance). The alternative would be to give the user a way to separately control |
| 79 | /// the generics of the builder struct, which would be very awkward to use and complex to document. |
| 80 | /// We could just include the field anyway, as `Option<T>`, but this is wasteful of space, and it |
| 81 | /// seems good to explicitly suppress the existence of a variable that won't be set or read. |
| 82 | Phantom(&'a syn::Type), |
| 83 | } |
| 84 | |
| 85 | impl<'a> BuilderFieldType<'a> { |
| 86 | /// Obtain type information for the builder field setter |
| 87 | /// |
| 88 | /// Return value: |
| 89 | /// * `.0`: type of the argument to the setter function |
| 90 | /// (before application of `strip_option`, `into`) |
| 91 | /// * `.1`: whether the builder field is `Option<type>` rather than just `type` |
| 92 | pub fn setter_type_info(&'a self) -> (&'a syn::Type, bool) { |
| 93 | match self { |
| 94 | BuilderFieldType::Optional(ty: &&Type) => (ty, true), |
| 95 | BuilderFieldType::Precise(ty: &&Type) => (ty, false), |
| 96 | BuilderFieldType::Phantom(_ty: &&Type) => panic!("phantom fields should never have setters" ), |
| 97 | } |
| 98 | } |
| 99 | |
| 100 | fn with_crate_root(&'a self, crate_root: &'a syn::Path) -> BuilderFieldTypeWithCrateRoot<'a> { |
| 101 | BuilderFieldTypeWithCrateRoot { |
| 102 | crate_root, |
| 103 | field_type: self, |
| 104 | } |
| 105 | } |
| 106 | } |
| 107 | |
| 108 | struct BuilderFieldTypeWithCrateRoot<'a> { |
| 109 | crate_root: &'a syn::Path, |
| 110 | field_type: &'a BuilderFieldType<'a>, |
| 111 | } |
| 112 | |
| 113 | impl<'a> ToTokens for BuilderFieldTypeWithCrateRoot<'a> { |
| 114 | fn to_tokens(&self, tokens: &mut TokenStream) { |
| 115 | let crate_root: &'a Path = self.crate_root; |
| 116 | match self.field_type { |
| 117 | BuilderFieldType::Optional(ty: &&Type) => tokens.append_all(iter:quote!( |
| 118 | #crate_root::export::core::option::Option<#ty> |
| 119 | )), |
| 120 | BuilderFieldType::Precise(ty: &&Type) => ty.to_tokens(tokens), |
| 121 | BuilderFieldType::Phantom(ty: &&Type) => tokens.append_all(iter:quote!( |
| 122 | #crate_root::export::core::marker::PhantomData<#ty> |
| 123 | )), |
| 124 | } |
| 125 | } |
| 126 | } |
| 127 | |
| 128 | /// Helper macro for unit tests. This is _only_ public in order to be accessible |
| 129 | /// from doc-tests too. |
| 130 | #[cfg (test)] // This contains a Box::leak, so is suitable only for tests |
| 131 | #[doc (hidden)] |
| 132 | #[macro_export ] |
| 133 | macro_rules! default_builder_field { |
| 134 | () => {{ |
| 135 | BuilderField { |
| 136 | // Deliberately don't use the default value here - make sure |
| 137 | // that all test cases are passing crate_root through properly. |
| 138 | crate_root: &parse_quote!(::db), |
| 139 | field_ident: &syn::Ident::new("foo" , ::proc_macro2::Span::call_site()), |
| 140 | field_type: BuilderFieldType::Optional(Box::leak(Box::new(parse_quote!(String)))), |
| 141 | field_visibility: ::std::borrow::Cow::Owned(parse_quote!(pub)), |
| 142 | attrs: &[parse_quote!(#[some_attr])], |
| 143 | } |
| 144 | }}; |
| 145 | } |
| 146 | |
| 147 | #[cfg (test)] |
| 148 | mod tests { |
| 149 | #[allow (unused_imports)] |
| 150 | use super::*; |
| 151 | |
| 152 | #[test ] |
| 153 | fn setter_enabled() { |
| 154 | let field = default_builder_field!(); |
| 155 | |
| 156 | assert_eq!( |
| 157 | quote!(#field).to_string(), |
| 158 | quote!( |
| 159 | #[some_attr] pub foo: ::db::export::core::option::Option<String>, |
| 160 | ) |
| 161 | .to_string() |
| 162 | ); |
| 163 | } |
| 164 | |
| 165 | #[test ] |
| 166 | fn setter_disabled() { |
| 167 | let mut field = default_builder_field!(); |
| 168 | field.field_visibility = Cow::Owned(syn::Visibility::Inherited); |
| 169 | field.field_type = match field.field_type { |
| 170 | BuilderFieldType::Optional(ty) => BuilderFieldType::Phantom(ty), |
| 171 | _ => panic!(), |
| 172 | }; |
| 173 | |
| 174 | assert_eq!( |
| 175 | quote!(#field).to_string(), |
| 176 | quote!( |
| 177 | #[some_attr] |
| 178 | foo: ::db::export::core::marker::PhantomData<String>, |
| 179 | ) |
| 180 | .to_string() |
| 181 | ); |
| 182 | } |
| 183 | |
| 184 | #[test ] |
| 185 | fn private_field() { |
| 186 | let private = Cow::Owned(syn::Visibility::Inherited); |
| 187 | let mut field = default_builder_field!(); |
| 188 | field.field_visibility = private; |
| 189 | |
| 190 | assert_eq!( |
| 191 | quote!(#field).to_string(), |
| 192 | quote!( |
| 193 | #[some_attr] |
| 194 | foo: ::db::export::core::option::Option<String>, |
| 195 | ) |
| 196 | .to_string() |
| 197 | ); |
| 198 | } |
| 199 | } |
| 200 | |