bon_macros/builder/builder_gen/
getters.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
use super::{BuilderGenCtx, NamedMember};
use crate::util::prelude::*;

pub(crate) struct GettersCtx<'a> {
    base: &'a BuilderGenCtx,
    member: &'a NamedMember,
}

struct GetterItem {
    name: syn::Ident,
    vis: syn::Visibility,
    docs: Vec<syn::Attribute>,
}

impl<'a> GettersCtx<'a> {
    pub(crate) fn new(base: &'a BuilderGenCtx, member: &'a NamedMember) -> Self {
        Self { base, member }
    }

    pub(crate) fn getter_methods(&self) -> TokenStream {
        let GetterItem { name, vis, docs } = match GetterItem::new(self) {
            Some(item) => item,
            None => return quote! {},
        };

        let index = &self.member.index;
        let ty = self.member.underlying_norm_ty();

        let (return_type, body) = if self.member.is_required() {
            (
                quote! { &#ty },
                quote! {
                    unsafe {
                        // SAFETY: this code is runs in a method that has a where
                        // bound that ensures the member was set.
                        ::core::option::Option::unwrap_unchecked(
                            self.__unsafe_private_named.#index.as_ref()
                        )
                    }
                },
            )
        } else {
            (
                // We are not using the fully qualified path to `Option` here
                // to make function signature in IDE popus shorter and more
                // readable.
                quote! { Option<&#ty> },
                quote! { self.__unsafe_private_named.#index.as_ref() },
            )
        };

        let state_var = &self.base.state_var;
        let member_pascal = &self.member.name.pascal;
        let state_mod = &self.base.state_mod.ident;

        quote! {
            #( #docs )*
            #[allow(
                // This is intentional. We want the builder syntax to compile away
                clippy::inline_always,
                clippy::missing_const_for_fn,
            )]
            #[inline(always)]
            #[must_use = "this method has no side effects; it only returns a value"]
            #vis fn #name(&self) -> #return_type
            where
                #state_var::#member_pascal: #state_mod::IsSet,
            {
                #body
            }
        }
    }
}

impl GetterItem {
    fn new(ctx: &GettersCtx<'_>) -> Option<Self> {
        let GettersCtx { member, base } = ctx;

        let config = member.config.getter.as_ref()?;

        Some(Self {
            name: config.name().cloned().unwrap_or_else(|| {
                syn::Ident::new(
                    &format!("get_{}", member.name.snake.raw_name()),
                    member.name.snake.span(),
                )
            }),
            vis: config.vis().unwrap_or(&base.builder_type.vis).clone(),
            docs: config.docs().map(<[_]>::to_vec).unwrap_or_else(|| {
                let header = format!(
                    "_**Getter.**_ Returns `{}`, which must be set before calling this method.\n\n",
                    member.name.snake,
                );

                std::iter::once(syn::parse_quote!(#[doc = #header]))
                    .chain(member.docs.iter().cloned())
                    .collect()
            }),
        })
    }
}