1use std::borrow::Cow;
2
3use proc_macro2::TokenStream;
4use quote::{ToTokens, TokenStreamExt};
5use 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)]
33pub 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
46impl<'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
58impl<'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)]
69pub 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
86impl<'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
109struct BuilderFieldTypeWithCrateRoot<'a> {
110 crate_root: &'a syn::Path,
111 field_type: &'a BuilderFieldType<'a>,
112}
113
114impl<'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]
134macro_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)]
149mod 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