typed_builder_macro/
util.rs

1use std::iter;
2
3use proc_macro2::{Ident, Span, TokenStream, TokenTree};
4use quote::{format_ident, quote, ToTokens};
5use syn::{
6    parenthesized,
7    parse::{Parse, ParseStream, Parser},
8    punctuated::Punctuated,
9    spanned::Spanned,
10    token, Attribute, Error, Pat, PatIdent, Token,
11};
12
13pub fn path_to_single_string(path: &syn::Path) -> Option<String> {
14    if path.leading_colon.is_some() {
15        return None;
16    }
17    let mut it = path.segments.iter();
18    let segment = it.next()?;
19    if it.next().is_some() {
20        // Multipart path
21        return None;
22    }
23    if segment.arguments != syn::PathArguments::None {
24        return None;
25    }
26    Some(segment.ident.to_string())
27}
28
29pub fn ident_to_type(ident: syn::Ident) -> syn::Type {
30    let mut path = syn::Path {
31        leading_colon: None,
32        segments: Default::default(),
33    };
34    path.segments.push(syn::PathSegment {
35        ident,
36        arguments: Default::default(),
37    });
38    syn::Type::Path(syn::TypePath { qself: None, path })
39}
40
41pub fn empty_type() -> syn::Type {
42    syn::TypeTuple {
43        paren_token: Default::default(),
44        elems: Default::default(),
45    }
46    .into()
47}
48
49pub fn type_tuple(elems: impl Iterator<Item = syn::Type>) -> syn::TypeTuple {
50    let mut result = syn::TypeTuple {
51        paren_token: Default::default(),
52        elems: elems.collect(),
53    };
54    if !result.elems.empty_or_trailing() {
55        result.elems.push_punct(Default::default());
56    }
57    result
58}
59
60pub fn empty_type_tuple() -> syn::TypeTuple {
61    syn::TypeTuple {
62        paren_token: Default::default(),
63        elems: Default::default(),
64    }
65}
66
67pub fn modify_types_generics_hack<F>(ty_generics: &syn::TypeGenerics, mut mutator: F) -> syn::AngleBracketedGenericArguments
68where
69    F: FnMut(&mut syn::punctuated::Punctuated<syn::GenericArgument, syn::token::Comma>),
70{
71    let mut abga: syn::AngleBracketedGenericArguments =
72        syn::parse2(ty_generics.to_token_stream()).unwrap_or_else(|_| syn::AngleBracketedGenericArguments {
73            colon2_token: None,
74            lt_token: Default::default(),
75            args: Default::default(),
76            gt_token: Default::default(),
77        });
78    mutator(&mut abga.args);
79    abga
80}
81
82pub fn strip_raw_ident_prefix(mut name: String) -> String {
83    if name.starts_with("r#") {
84        name.replace_range(0..2, "");
85    }
86    name
87}
88
89pub fn first_visibility(visibilities: &[Option<&syn::Visibility>]) -> proc_macro2::TokenStream {
90    let vis = visibilities
91        .iter()
92        .flatten()
93        .next()
94        .expect("need at least one visibility in the list");
95
96    vis.to_token_stream()
97}
98
99pub fn public_visibility() -> syn::Visibility {
100    syn::Visibility::Public(syn::token::Pub::default())
101}
102
103pub fn expr_to_lit_string(expr: &syn::Expr) -> Result<String, Error> {
104    match expr {
105        syn::Expr::Lit(lit) => match &lit.lit {
106            syn::Lit::Str(str) => Ok(str.value()),
107            _ => Err(Error::new_spanned(expr, "attribute only allows str values")),
108        },
109        _ => Err(Error::new_spanned(expr, "attribute only allows str values")),
110    }
111}
112
113pub enum AttrArg {
114    Flag(Ident),
115    KeyValue(KeyValue),
116    Sub(SubAttr),
117    Not { not: Token![!], name: Ident },
118}
119
120impl AttrArg {
121    pub fn name(&self) -> &Ident {
122        match self {
123            AttrArg::Flag(name) => name,
124            AttrArg::KeyValue(KeyValue { name, .. }) => name,
125            AttrArg::Sub(SubAttr { name, .. }) => name,
126            AttrArg::Not { name, .. } => name,
127        }
128    }
129
130    pub fn incorrect_type(&self) -> syn::Error {
131        let message = match self {
132            AttrArg::Flag(name) => format!("{:?} is not supported as a flag", name.to_string()),
133            AttrArg::KeyValue(KeyValue { name, .. }) => format!("{:?} is not supported as key-value", name.to_string()),
134            AttrArg::Sub(SubAttr { name, .. }) => format!("{:?} is not supported as nested attribute", name.to_string()),
135            AttrArg::Not { name, .. } => format!("{:?} cannot be nullified", name.to_string()),
136        };
137        syn::Error::new_spanned(self, message)
138    }
139
140    pub fn flag(self) -> syn::Result<Ident> {
141        if let Self::Flag(name) = self {
142            Ok(name)
143        } else {
144            Err(self.incorrect_type())
145        }
146    }
147
148    pub fn key_value(self) -> syn::Result<KeyValue> {
149        if let Self::KeyValue(key_value) = self {
150            Ok(key_value)
151        } else {
152            Err(self.incorrect_type())
153        }
154    }
155
156    pub fn key_value_or_not(self) -> syn::Result<Option<KeyValue>> {
157        match self {
158            Self::KeyValue(key_value) => Ok(Some(key_value)),
159            Self::Not { .. } => Ok(None),
160            _ => Err(self.incorrect_type()),
161        }
162    }
163
164    pub fn sub_attr(self) -> syn::Result<SubAttr> {
165        if let Self::Sub(sub_attr) = self {
166            Ok(sub_attr)
167        } else {
168            Err(self.incorrect_type())
169        }
170    }
171
172    pub fn apply_flag_to_field(self, field: &mut Option<Span>, caption: &str) -> syn::Result<()> {
173        match self {
174            AttrArg::Flag(flag) => {
175                if field.is_none() {
176                    *field = Some(flag.span());
177                    Ok(())
178                } else {
179                    Err(Error::new(
180                        flag.span(),
181                        format!("Illegal setting - field is already {caption}"),
182                    ))
183                }
184            }
185            AttrArg::Not { .. } => {
186                *field = None;
187                Ok(())
188            }
189            _ => Err(self.incorrect_type()),
190        }
191    }
192
193    pub fn apply_potentialy_empty_sub_to_field<T: ApplyMeta>(
194        self,
195        field: &mut Option<T>,
196        caption: &str,
197        init: impl FnOnce(Span) -> T,
198    ) -> syn::Result<()> {
199        match self {
200            AttrArg::Sub(sub) => {
201                if field.is_none() {
202                    let mut value = init(sub.span());
203                    value.apply_sub_attr(sub)?;
204                    *field = Some(value);
205                    Ok(())
206                } else {
207                    Err(Error::new(
208                        sub.span(),
209                        format!("Illegal setting - field is already {caption}"),
210                    ))
211                }
212            }
213            AttrArg::Flag(flag) => {
214                if field.is_none() {
215                    *field = Some(init(flag.span()));
216                    Ok(())
217                } else {
218                    Err(Error::new(
219                        flag.span(),
220                        format!("Illegal setting - field is already {caption}"),
221                    ))
222                }
223            }
224            AttrArg::Not { .. } => {
225                *field = None;
226                Ok(())
227            }
228            _ => Err(self.incorrect_type()),
229        }
230    }
231}
232
233pub struct KeyValue {
234    pub name: Ident,
235    pub eq: Token![=],
236    pub value: TokenStream,
237}
238
239impl KeyValue {
240    pub fn parse_value<T: Parse>(self) -> syn::Result<T> {
241        syn::parse2(self.value)
242    }
243}
244
245impl ToTokens for KeyValue {
246    fn to_tokens(&self, tokens: &mut TokenStream) {
247        self.name.to_tokens(tokens);
248        self.eq.to_tokens(tokens);
249        self.value.to_tokens(tokens);
250    }
251}
252
253impl Parse for KeyValue {
254    fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
255        Ok(Self {
256            name: input.parse()?,
257            eq: input.parse()?,
258            value: input.parse()?,
259        })
260    }
261}
262
263pub struct SubAttr {
264    pub name: Ident,
265    pub paren: token::Paren,
266    pub args: TokenStream,
267}
268
269impl SubAttr {
270    pub fn args<T: Parse>(self) -> syn::Result<impl IntoIterator<Item = T>> {
271        Punctuated::<T, Token![,]>::parse_terminated.parse2(self.args)
272    }
273    pub fn undelimited<T: Parse>(self) -> syn::Result<impl IntoIterator<Item = T>> {
274        (|p: ParseStream| iter::from_fn(|| (!p.is_empty()).then(|| p.parse())).collect::<syn::Result<Vec<T>>>()).parse2(self.args)
275    }
276}
277
278impl ToTokens for SubAttr {
279    fn to_tokens(&self, tokens: &mut TokenStream) {
280        self.name.to_tokens(tokens);
281        self.paren.surround(tokens, |t| self.args.to_tokens(t));
282    }
283}
284
285fn get_cursor_after_parsing<P: Parse + Spanned>(input: syn::parse::ParseBuffer) -> syn::Result<syn::buffer::Cursor> {
286    let parse_attempt: P = input.parse()?;
287    let cursor = input.cursor();
288    if cursor.eof() || input.peek(Token![,]) {
289        Ok(cursor)
290    } else {
291        Err(syn::Error::new(
292            parse_attempt.span(),
293            "does not end with comma or end of section",
294        ))
295    }
296}
297
298fn get_token_stream_up_to_cursor(input: syn::parse::ParseStream, cursor: syn::buffer::Cursor) -> syn::Result<TokenStream> {
299    Ok(core::iter::from_fn(|| {
300        if input.cursor() < cursor {
301            input.parse::<TokenTree>().ok()
302        } else {
303            None
304        }
305    })
306    .collect())
307}
308
309impl Parse for AttrArg {
310    fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
311        if input.peek(Token![!]) {
312            Ok(Self::Not {
313                not: input.parse()?,
314                name: input.parse()?,
315            })
316        } else {
317            let name = input.parse()?;
318            if input.peek(Token![,]) || input.is_empty() {
319                Ok(Self::Flag(name))
320            } else if input.peek(token::Paren) {
321                let args;
322                Ok(Self::Sub(SubAttr {
323                    name,
324                    paren: parenthesized!(args in input),
325                    args: args.parse()?,
326                }))
327            } else if input.peek(Token![=]) {
328                // Try parsing as a type first, because it _should_ be simpler
329
330                Ok(Self::KeyValue(KeyValue {
331                    name,
332                    eq: input.parse()?,
333                    value: {
334                        let cursor = get_cursor_after_parsing::<syn::Type>(input.fork())
335                            .or_else(|_| get_cursor_after_parsing::<syn::Expr>(input.fork()))?;
336                        get_token_stream_up_to_cursor(input, cursor)?
337                    },
338                }))
339            } else {
340                Err(input.error("expected !<ident>, <ident>=<value> or <ident>(…)"))
341            }
342        }
343    }
344}
345
346impl ToTokens for AttrArg {
347    fn to_tokens(&self, tokens: &mut TokenStream) {
348        match self {
349            AttrArg::Flag(flag) => flag.to_tokens(tokens),
350            AttrArg::KeyValue(kv) => kv.to_tokens(tokens),
351            AttrArg::Sub(sub) => sub.to_tokens(tokens),
352            AttrArg::Not { not, name } => {
353                not.to_tokens(tokens);
354                name.to_tokens(tokens);
355            }
356        }
357    }
358}
359
360pub trait ApplyMeta {
361    fn apply_meta(&mut self, expr: AttrArg) -> Result<(), Error>;
362
363    fn apply_sub_attr(&mut self, sub_attr: SubAttr) -> syn::Result<()> {
364        for arg in sub_attr.args()? {
365            self.apply_meta(arg)?;
366        }
367        Ok(())
368    }
369
370    fn apply_subsections(&mut self, list: &syn::MetaList) -> syn::Result<()> {
371        if list.tokens.is_empty() {
372            return Err(syn::Error::new_spanned(list, "Expected builder(…)"));
373        }
374
375        let parser = syn::punctuated::Punctuated::<_, syn::token::Comma>::parse_terminated;
376        let exprs = parser.parse2(list.tokens.clone())?;
377        for expr in exprs {
378            self.apply_meta(expr)?;
379        }
380
381        Ok(())
382    }
383
384    fn apply_attr(&mut self, attr: &Attribute) -> syn::Result<()> {
385        match &attr.meta {
386            syn::Meta::List(list) => self.apply_subsections(list),
387            meta => Err(Error::new_spanned(meta, "Expected builder(…)")),
388        }
389    }
390}
391
392pub fn pat_to_ident(i: usize, pat: &Pat) -> Ident {
393    if let Pat::Ident(PatIdent { ident, .. }) = pat {
394        ident.clone()
395    } else {
396        format_ident!("__{i}", span = pat.span())
397    }
398}
399
400pub fn phantom_data_for_generics(generics: &syn::Generics) -> proc_macro2::TokenStream {
401    let phantom_generics = generics.params.iter().filter_map(|param| match param {
402        syn::GenericParam::Lifetime(lifetime) => {
403            let lifetime = &lifetime.lifetime;
404            Some(quote!(&#lifetime ()))
405        }
406        syn::GenericParam::Type(ty) => {
407            let ty = &ty.ident;
408            Some(ty.to_token_stream())
409        }
410        syn::GenericParam::Const(_cnst) => None,
411    });
412    quote!(::core::marker::PhantomData<(#( ::core::marker::PhantomData<#phantom_generics> ),*)>)
413}