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