derive_builder_core/
builder_field.rs

1use std::borrow::Cow;
2
3use proc_macro2::TokenStream;
4use 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)]
32pub 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
45impl<'a> ToTokens for BuilderField<'a> {
46    fn to_tokens(&self, tokens: &mut TokenStream) {
47        let ident = self.field_ident;
48        let vis = &self.field_visibility;
49        let ty = &self.field_type.with_crate_root(self.crate_root);
50        let attrs = self.attrs;
51        tokens.append_all(quote!(
52            #(#attrs)* #vis #ident: #ty,
53        ));
54    }
55}
56
57impl<'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 = self.field_ident;
61        let crate_root = 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)]
68pub 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
85impl<'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) => (ty, true),
95            BuilderFieldType::Precise(ty) => (ty, false),
96            BuilderFieldType::Phantom(_ty) => 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
108struct BuilderFieldTypeWithCrateRoot<'a> {
109    crate_root: &'a syn::Path,
110    field_type: &'a BuilderFieldType<'a>,
111}
112
113impl<'a> ToTokens for BuilderFieldTypeWithCrateRoot<'a> {
114    fn to_tokens(&self, tokens: &mut TokenStream) {
115        let crate_root = self.crate_root;
116        match self.field_type {
117            BuilderFieldType::Optional(ty) => tokens.append_all(quote!(
118                #crate_root::export::core::option::Option<#ty>
119            )),
120            BuilderFieldType::Precise(ty) => ty.to_tokens(tokens),
121            BuilderFieldType::Phantom(ty) => tokens.append_all(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]
133macro_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)]
148mod 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}