Skip to main content

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