bon_macros/builder/builder_gen/member/config/with/
closure.rs

1use crate::parsing::SimpleClosure;
2use crate::util::prelude::*;
3use darling::FromMeta;
4
5const INVALID_RETURN_TYPE_ERROR: &str = "\
6expected one of the following:
7
8(1) no return type annotation;
9    this means the closure is expected to return a value of the same type
10    as the member's underlying type(*);
11
12(2) `-> *Result<_, {{ErrorType}}>` or `-> *Result<_>` return type annotation;
13    this means the closure is expected to return a `Result` where the `Ok`
14    variant is of the same type as the member's underlying type(*); this syntax
15    allows you to define a fallbile setter (one that returns a `Result<Builder>`);
16
17    the `_` placeholder must be spelled literally to mark the underlying type(*)
18    of the member; an optional second generic parameter for the error type is allowed;
19
20    the return type doesn't have to be named `Result` exactly, the only requirement is
21    that it must have the `Result` suffix; for example if you have a type alias
22    `ApiResult<_>`, then it'll work fine;
23
24(*) underlying type is the type of the member stripped from the `Option<T>` wrapper
25    if this member is of `Option<T>` type and no `#[builder(required)]` annotation
26    is present";
27
28#[derive(Debug)]
29pub(crate) struct SetterClosure {
30    pub(crate) inputs: Vec<SetterClosureInput>,
31    pub(crate) body: Box<syn::Expr>,
32    pub(crate) output: Option<SetterClosureOutput>,
33}
34
35#[derive(Debug)]
36pub(crate) struct SetterClosureOutput {
37    pub(crate) result_path: syn::Path,
38    pub(crate) err_ty: Option<syn::Type>,
39}
40
41#[derive(Debug)]
42pub(crate) struct SetterClosureInput {
43    pub(crate) pat: syn::PatIdent,
44    pub(crate) ty: Box<syn::Type>,
45}
46
47impl FromMeta for SetterClosure {
48    fn from_meta(item: &syn::Meta) -> Result<Self> {
49        let closure = SimpleClosure::from_meta(item)?;
50
51        let inputs = closure
52            .inputs
53            .into_iter()
54            .map(|input| {
55                Ok(SetterClosureInput {
56                    ty: input.ty.ok_or_else(|| {
57                        err!(&input.pat, "expected a type for the setter input parameter")
58                    })?,
59                    pat: input.pat,
60                })
61            })
62            .collect::<Result<_>>()?;
63
64        let return_type = match closure.output {
65            syn::ReturnType::Default => None,
66            syn::ReturnType::Type(_, ty) => {
67                let err = || err!(&ty, "{INVALID_RETURN_TYPE_ERROR}");
68
69                let ty = ty
70                    .as_generic_angle_bracketed_path(|last_segment| {
71                        // We allow for arbitrary `Result` type variations
72                        // including custom type aliases like `ApiResult<_>`
73                        last_segment.to_string().ends_with("Result")
74                    })
75                    .ok_or_else(err)?;
76
77                if !(1..=2).contains(&ty.args.len()) {
78                    return Err(err());
79                }
80
81                let mut args = ty.args.iter();
82                let ok_ty = args.next().ok_or_else(err)?;
83
84                if !matches!(ok_ty, syn::GenericArgument::Type(syn::Type::Infer(_))) {
85                    return Err(err());
86                }
87
88                let err_ty = args
89                    .next()
90                    .map(|arg| match arg {
91                        syn::GenericArgument::Type(ty) => Ok(ty.clone()),
92                        _ => Err(err()),
93                    })
94                    .transpose()?;
95
96                let mut result_path = ty.path.clone();
97
98                // We store the error type of the result separately.
99                // Strip the generic arguments, because we only care
100                // about the path of the `Result` in `result_path` field.
101                result_path
102                    .segments
103                    .last_mut()
104                    .expect("BUG: segments can't be empty")
105                    .arguments = syn::PathArguments::None;
106
107                Some(SetterClosureOutput {
108                    result_path,
109                    err_ty,
110                })
111            }
112        };
113
114        Ok(Self {
115            inputs,
116            body: closure.body,
117            output: return_type,
118        })
119    }
120}