bon_macros/builder/builder_gen/top_level_config/
on.rs1use crate::util::prelude::*;
2use darling::FromMeta;
3use syn::parse::Parse;
4use syn::spanned::Spanned;
5use syn::visit::Visit;
6
7#[derive(Debug)]
8pub(crate) struct OnConfig {
9 pub(crate) type_pattern: syn::Type,
10 pub(crate) into: darling::util::Flag,
11 pub(crate) overwritable: darling::util::Flag,
12 pub(crate) required: darling::util::Flag,
13}
14
15impl Parse for OnConfig {
16 fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result<Self> {
17 let type_pattern = input.parse()?;
18
19 let _ = input.parse::<syn::Token![,]>()?;
20 let rest: TokenStream = input.parse()?;
21
22 #[derive(FromMeta)]
23 struct Parsed {
24 into: darling::util::Flag,
25 overwritable: darling::util::Flag,
26 required: darling::util::Flag,
27 }
28
29 let parsed = Parsed::from_meta(&syn::parse_quote!(on(#rest)))?;
30
31 if !cfg!(feature = "experimental-overwritable") && parsed.overwritable.is_present() {
32 return Err(syn::Error::new(
33 parsed.overwritable.span(),
34 "🔬 `overwritable` attribute is experimental and requires \
35 \"experimental-overwritable\" cargo feature to be enabled; \
36 we would be glad to make this attribute stable if you find it useful; \
37 please leave a 👍 reaction under the issue https://github.com/elastio/bon/issues/149 \
38 to help us measure the demand for this feature; it would be \
39 double-awesome if you could also describe your use case in \
40 a comment under the issue for us to understand how it's used \
41 in practice",
42 ));
43 }
44
45 {
46 let Parsed {
51 into,
52 overwritable,
53 required,
54 } = &parsed;
55 let flags = [
56 ("into", into),
57 ("overwritable", overwritable),
58 ("required", required),
59 ];
60
61 if flags.iter().all(|(_, flag)| !flag.is_present()) {
62 let flags = flags.iter().map(|(name, _)| format!("`{name}`")).join(", ");
63 let err = format!(
64 "this #[builder(on(type_pattern, ...))] contains no options \
65 to override the default behavior for the selected setters \
66 like {flags}, so it does nothing"
67 );
68
69 return Err(syn::Error::new_spanned(&rest, err));
70 }
71 }
72
73 struct FindAttr {
74 attr: Option<Span>,
75 }
76
77 impl Visit<'_> for FindAttr {
78 fn visit_attribute(&mut self, attr: &'_ syn::Attribute) {
79 self.attr.get_or_insert(attr.span());
80 }
81 }
82
83 let mut find_attr = FindAttr { attr: None };
84 find_attr.visit_type(&type_pattern);
85
86 if let Some(attr) = find_attr.attr {
87 return Err(syn::Error::new(
88 attr,
89 "nested attributes are not allowed in the type pattern of \
90 #[builder(on(type_pattern, ...))]",
91 ));
92 }
93
94 let type_pattern_matches_itself = type_pattern.matches(&type_pattern)?;
97
98 assert!(
99 type_pattern_matches_itself,
100 "BUG: the type pattern does not match itself: {type_pattern:#?}"
101 );
102
103 let Parsed {
104 into,
105 overwritable,
106 required,
107 } = parsed;
108
109 Ok(Self {
110 type_pattern,
111 into,
112 overwritable,
113 required,
114 })
115 }
116}
117
118impl FromMeta for OnConfig {
119 fn from_meta(meta: &syn::Meta) -> Result<Self> {
120 let meta = match meta {
121 syn::Meta::List(meta) => meta,
122 _ => bail!(
123 meta,
124 "expected an attribute of form `on(type_pattern, ...)`"
125 ),
126 };
127
128 let me = syn::parse2(meta.tokens.clone())?;
129
130 Ok(me)
131 }
132}