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 | |