1mod docs;
2mod item_sig;
3mod simple_closure;
4mod spanned_key;
56pub(crate) use docs::*;
7pub(crate) use item_sig::*;
8pub(crate) use simple_closure::*;
9pub(crate) use spanned_key::*;
1011use crate::util::prelude::*;
12use darling::FromMeta;
13use syn::parse::Parser;
14use syn::punctuated::Punctuated;
15use syn::spanned::Spanned;
1617pub(crate) fn parse_non_empty_paren_meta_list<T: FromMeta>(meta: &syn::Meta) -> Result<T> {
18 require_non_empty_paren_meta_list_or_name_value(meta)?;
19 T::from_meta(meta)
20}
2122pub(crate) fn require_non_empty_paren_meta_list_or_name_value(meta: &syn::Meta) -> Result {
23match meta {
24 syn::Meta::List(meta) => {
25 meta.require_parens_delim()?;
2627if meta.tokens.is_empty() {
28bail!(
29&meta.delimiter.span().join(),
30"expected parameters in parentheses"
31);
32 }
33 }
34 syn::Meta::Path(path) => bail!(
35&meta,
36"this empty `{0}` attribute is unexpected; \
37 remove it or pass parameters in parentheses: \
38 `{0}(...)`",
39 darling::util::path_to_string(path)
40 ),
41 syn::Meta::NameValue(_) => {}
42 }
4344Ok(())
45}
4647/// Utility for parsing with `#[darling(with = ...)]` attribute that allows to
48/// parse an arbitrary sequence of items inside of parentheses. For example
49/// `foo(a, b, c)`, where `a`, `b`, and `c` are of type `T` and `,` is represented
50/// by the token type `P`.
51#[allow(dead_code)]
52pub(crate) fn parse_paren_meta_list_with_terminated<T, P>(
53 meta: &syn::Meta,
54) -> Result<Punctuated<T, P>>
55where
56T: syn::parse::Parse,
57 P: syn::parse::Parse,
58{
59let item = std::any::type_name::<T>();
60let punct = std::any::type_name::<P>();
6162let name = |val: &str| {
63format!(
64"'{}'",
65 val.rsplit("::").next().unwrap_or(val).to_lowercase()
66 )
67 };
6869let meta = match meta {
70 syn::Meta::List(meta) => meta,
71_ => bail!(
72&meta,
73"expected a list of {} separated by {}",
74 name(item),
75 name(punct),
76 ),
77 };
7879 meta.require_parens_delim()?;
8081let punctuated = Punctuated::parse_terminated.parse2(meta.tokens.clone())?;
8283Ok(punctuated)
84}
8586fn parse_path_mod_style(meta: &syn::Meta) -> Result<syn::Path> {
87let expr = match meta {
88 syn::Meta::NameValue(meta) => &meta.value,
89_ => bail!(meta, "expected a simple path, like `foo::bar`"),
90 };
9192Ok(expr.require_path_mod_style()?.clone())
93}
9495pub(crate) fn parse_bon_crate_path(meta: &syn::Meta) -> Result<syn::Path> {
96let path = parse_path_mod_style(meta)?;
9798let prefix = &path
99 .segments
100 .first()
101 .ok_or_else(|| err!(&path, "path must have at least one segment"))?
102.ident;
103104let is_absolute = path.leading_colon.is_some() || prefix == "crate" || prefix == "$crate";
105106if is_absolute {
107return Ok(path);
108 }
109110if prefix == "super" || prefix == "self" {
111bail!(
112&path,
113"path must not be relative; specify the path that starts with `crate::` \
114 instead; if you want to refer to a reexport from an external crate then \
115 use a leading colon like `::crate_name::reexport::path::bon`"
116)
117 }
118119let path_str = darling::util::path_to_string(&path);
120121bail!(
122&path,
123"path must be absolute; if you want to refer to a reexport from an external \
124 crate then add a leading colon like `::{path_str}`; if the path leads to a module \
125 in the current crate, then specify the absolute path with `crate` like \
126 `crate::reexport::path::bon` or `$crate::reexport::path::bon` (if within a macro)"
127)
128}
129130// Lint from nightly. `&Option<T>` is used to reduce syntax at the callsite
131#[allow(unknown_lints, clippy::ref_option)]
132pub(crate) fn reject_syntax<T: Spanned>(name: &'static str, syntax: &Option<T>) -> Result {
133if let Some(syntax) = syntax {
134bail!(syntax, "{name} is not allowed here")
135 }
136137Ok(())
138}