bon_macros/builder/builder_gen/member/config/
setters.rs

1use crate::parsing::{ItemSigConfig, ItemSigConfigParsing, SpannedKey};
2use crate::util::prelude::*;
3use darling::ast::NestedMeta;
4use darling::FromMeta;
5use syn::punctuated::Punctuated;
6
7const DOCS_CONTEXT: &str = "builder struct's impl block";
8
9fn parse_setter_fn(meta: &syn::Meta) -> Result<SpannedKey<ItemSigConfig>> {
10    let params = ItemSigConfigParsing {
11        meta,
12        reject_self_mentions: Some(DOCS_CONTEXT),
13    }
14    .parse()?;
15
16    SpannedKey::new(meta.path(), params)
17}
18
19fn parse_docs(meta: &syn::Meta) -> Result<SpannedKey<Vec<syn::Attribute>>> {
20    crate::parsing::parse_docs_without_self_mentions(DOCS_CONTEXT, meta)
21}
22
23#[derive(Debug, Default)]
24pub(crate) struct SettersConfig {
25    pub(crate) name: Option<SpannedKey<syn::Ident>>,
26    pub(crate) vis: Option<SpannedKey<syn::Visibility>>,
27    pub(crate) doc: SettersDocConfig,
28    pub(crate) fns: SettersFnsConfig,
29}
30
31impl FromMeta for SettersConfig {
32    fn from_meta(meta: &syn::Meta) -> Result<Self> {
33        let meta: Punctuated<syn::Meta, syn::Token![,]> =
34            crate::parsing::parse_paren_meta_list_with_terminated(meta)?;
35
36        let (docs, remaining_meta): (Vec<_>, Vec<_>) = meta
37            .into_iter()
38            .partition(|meta| meta.path().is_ident("doc"));
39
40        let doc = SettersDocConfig::from_docs_entries(docs)?;
41
42        #[derive(FromMeta)]
43        struct Parsed {
44            name: Option<SpannedKey<syn::Ident>>,
45            vis: Option<SpannedKey<syn::Visibility>>,
46
47            #[darling(flatten)]
48            fns: SettersFnsConfig,
49        }
50
51        let remaining_meta = remaining_meta
52            .into_iter()
53            .map(NestedMeta::Meta)
54            .collect::<Vec<_>>();
55
56        let parsed: Parsed = Parsed::from_list(&remaining_meta)?;
57
58        Ok(Self {
59            name: parsed.name,
60            vis: parsed.vis,
61            fns: parsed.fns,
62            doc,
63        })
64    }
65}
66
67#[derive(Debug, Default, FromMeta)]
68pub(crate) struct SettersFnsConfig {
69    /// Config for the setter that accepts the value of type T for a member of
70    /// type `Option<T>` or with `#[builder(default)]`.
71    ///
72    /// By default, it's named `{member}` without any prefix or suffix.
73    #[darling(default, with = parse_setter_fn, map = Some)]
74    pub(crate) some_fn: Option<SpannedKey<ItemSigConfig>>,
75
76    /// The setter that accepts the value of type `Option<T>` for a member of
77    /// type `Option<T>` or with `#[builder(default)]`.
78    ///
79    /// By default, it's named `maybe_{member}`.
80    #[darling(default, with = parse_setter_fn, map = Some)]
81    pub(crate) option_fn: Option<SpannedKey<ItemSigConfig>>,
82}
83
84#[derive(Debug, Default)]
85pub(crate) struct SettersDocConfig {
86    /// Overrides the content of the doc comments.
87    pub(crate) content: Option<SpannedKey<Vec<syn::Attribute>>>,
88
89    /// Overrides the look of the default value showcase in the docs header.
90    pub(crate) default: Option<SpannedKey<SettersDocDefaultConfig>>,
91}
92
93impl SettersDocConfig {
94    fn from_docs_entries(docs: Vec<syn::Meta>) -> Result<Self> {
95        let mut doc_config = None;
96        let mut doc_content = None;
97
98        for doc in docs {
99            match doc.require_list()?.delimiter {
100                syn::MacroDelimiter::Paren(_) => {
101                    if doc_config.is_some() {
102                        bail!(&doc, "repeated `doc(...)` attribute is not allowed");
103                    }
104                    doc_config = Some(doc);
105                }
106                syn::MacroDelimiter::Brace(_) => {
107                    if doc_content.is_some() {
108                        bail!(&doc, "repeated `doc {{...}}` attribute is not allowed");
109                    }
110                    doc_content = Some(doc);
111                }
112                syn::MacroDelimiter::Bracket(_) => {
113                    bail!(&doc, "wrong delimiter, expected doc(...) or doc {{...}}",);
114                }
115            }
116        }
117
118        #[derive(FromMeta)]
119        struct Parsed {
120            #[darling(with = crate::parsing::parse_non_empty_paren_meta_list)]
121            default: Option<SpannedKey<SettersDocDefaultConfig>>,
122        }
123
124        let config = doc_config
125            .as_ref()
126            .map(crate::parsing::parse_non_empty_paren_meta_list::<Parsed>)
127            .transpose()?;
128
129        let content = doc_content.as_ref().map(parse_docs).transpose()?;
130
131        let mut me = Self {
132            content,
133            default: None,
134        };
135
136        if let Some(Parsed { default }) = config {
137            me.default = default;
138            // More keys may be added here in the future
139        }
140
141        Ok(me)
142    }
143}
144
145#[derive(Debug, Default, FromMeta)]
146pub(crate) struct SettersDocDefaultConfig {
147    /// If `true`, the default value showcase in the docs header will be skipped.
148    pub(crate) skip: darling::util::Flag,
149}