bon_macros/parsing/
mod.rs

1mod bon_crate_path;
2mod docs;
3mod item_sig;
4mod simple_closure;
5mod spanned_key;
6
7pub(crate) use bon_crate_path::*;
8pub(crate) use docs::*;
9pub(crate) use item_sig::*;
10pub(crate) use simple_closure::*;
11pub(crate) use spanned_key::*;
12
13use crate::util::prelude::*;
14use darling::FromMeta;
15use syn::parse::Parser;
16use syn::punctuated::Punctuated;
17use syn::spanned::Spanned;
18
19pub(crate) fn parse_non_empty_paren_meta_list<T: FromMeta>(meta: &syn::Meta) -> Result<T> {
20    require_non_empty_paren_meta_list_or_name_value(meta)?;
21    T::from_meta(meta)
22}
23
24pub(crate) fn require_non_empty_paren_meta_list_or_name_value(meta: &syn::Meta) -> Result {
25    match meta {
26        syn::Meta::List(meta) => {
27            meta.require_parens_delim()?;
28
29            if meta.tokens.is_empty() {
30                bail!(
31                    &meta.delimiter.span().join(),
32                    "expected parameters in parentheses"
33                );
34            }
35        }
36        syn::Meta::Path(path) => bail!(
37            &meta,
38            "this empty `{0}` attribute is unexpected; \
39            remove it or pass parameters in parentheses: \
40            `{0}(...)`",
41            darling::util::path_to_string(path)
42        ),
43        syn::Meta::NameValue(_) => {}
44    }
45
46    Ok(())
47}
48
49/// Utility for parsing with `#[darling(with = ...)]` attribute that allows to
50/// parse an arbitrary sequence of items inside of parentheses. For example
51/// `foo(a, b, c)`, where `a`, `b`, and `c` are of type `T` and `,` is represented
52/// by the token type `P`.
53pub(crate) fn parse_paren_meta_list_with_terminated<T, P>(
54    meta: &syn::Meta,
55) -> Result<Punctuated<T, P>>
56where
57    T: syn::parse::Parse,
58    P: syn::parse::Parse,
59{
60    let item = std::any::type_name::<T>();
61    let punct = std::any::type_name::<P>();
62
63    let name = |val: &str| {
64        format!(
65            "'{}'",
66            val.rsplit("::").next().unwrap_or(val).to_lowercase()
67        )
68    };
69
70    let meta = match meta {
71        syn::Meta::List(meta) => meta,
72        _ => bail!(
73            &meta,
74            "expected a list of {} separated by {}",
75            name(item),
76            name(punct),
77        ),
78    };
79
80    meta.require_parens_delim()?;
81
82    let punctuated = Punctuated::parse_terminated.parse2(meta.tokens.clone())?;
83
84    Ok(punctuated)
85}
86
87fn parse_path_mod_style(meta: &syn::Meta) -> Result<syn::Path> {
88    let expr = match meta {
89        syn::Meta::NameValue(meta) => &meta.value,
90        _ => bail!(meta, "expected a simple path, like `foo::bar`"),
91    };
92
93    Ok(expr.require_path_mod_style()?.clone())
94}
95
96// Lint from nightly. `&Option<T>` is used to reduce syntax at the callsite
97#[allow(unknown_lints, clippy::ref_option)]
98pub(crate) fn reject_syntax<T: Spanned>(name: &'static str, syntax: &Option<T>) -> Result {
99    if let Some(syntax) = syntax {
100        bail!(syntax, "{name} is not allowed here")
101    }
102
103    Ok(())
104}