bon_macros/parsing/
mod.rs

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