bon_macros/parsing/
simple_closure.rs

1use super::reject_syntax;
2use crate::util::prelude::*;
3use darling::FromMeta;
4
5/// Utility type for parsing simple closure syntax that only allows [`syn::PatIdent`]
6/// inputs and rejects any attributes and prefix keywords like `async`, `move`, `for`
7/// on the closure.
8#[derive(Debug)]
9pub(crate) struct SimpleClosure {
10    pub(crate) inputs: Vec<SimpleClosureInput>,
11    pub(crate) body: Box<syn::Expr>,
12    pub(crate) output: syn::ReturnType,
13}
14
15#[derive(Debug)]
16pub(crate) struct SimpleClosureInput {
17    pub(crate) pat: syn::PatIdent,
18    pub(crate) ty: Option<Box<syn::Type>>,
19}
20
21impl FromMeta for SimpleClosure {
22    fn from_meta(meta: &syn::Meta) -> Result<Self> {
23        let err = || {
24            let path = darling::util::path_to_string(meta.path());
25            err!(
26                meta,
27                "expected a closure e.g. `{path} = |param: T| expression`"
28            )
29        };
30
31        let meta = match meta {
32            syn::Meta::NameValue(meta) => meta,
33            _ => return Err(err()),
34        };
35
36        let closure = match &meta.value {
37            syn::Expr::Closure(closure) => closure,
38            _ => return Err(err()),
39        };
40
41        reject_syntax("`for<...>` syntax", &closure.lifetimes)?;
42        reject_syntax("`const` keyword", &closure.constness)?;
43        reject_syntax("`static` keyword", &closure.movability)?;
44        reject_syntax("`async` keyword", &closure.asyncness)?;
45        reject_syntax("`move` keyword", &closure.capture)?;
46        reject_syntax("attribute", &closure.attrs.first())?;
47
48        let inputs = closure
49            .clone()
50            .inputs
51            .into_iter()
52            .map(|input| match input {
53                syn::Pat::Ident(pat) => SimpleClosureInput::from_pat_ident(pat),
54                syn::Pat::Type(pat) => SimpleClosureInput::from_pat_type(pat),
55                _ => bail!(&input, "expected a simple identifier pattern"),
56            })
57            .collect::<Result<_>>()?;
58
59        Ok(Self {
60            inputs,
61            body: closure.body.clone(),
62            output: closure.output.clone(),
63        })
64    }
65}
66
67impl SimpleClosureInput {
68    fn from_pat_ident(pat: syn::PatIdent) -> Result<Self> {
69        reject_syntax("attribute", &pat.attrs.first())?;
70        reject_syntax("`ref` keyword", &pat.by_ref)?;
71        Ok(Self { pat, ty: None })
72    }
73
74    fn from_pat_type(input: syn::PatType) -> Result<Self> {
75        reject_syntax("attribute", &input.attrs.first())?;
76
77        let ident = match *input.pat {
78            syn::Pat::Ident(pat) => Self::from_pat_ident(pat)?.pat,
79            _ => bail!(&input.pat, "expected a simple identifier pattern"),
80        };
81
82        Ok(Self {
83            pat: ident,
84            ty: Some(input.ty),
85        })
86    }
87}