miette_derive/
utils.rs

1use proc_macro2::TokenStream;
2use quote::{format_ident, quote, ToTokens};
3use syn::{
4    parse::{Parse, ParseStream},
5    spanned::Spanned,
6};
7
8pub(crate) enum MemberOrString {
9    Member(syn::Member),
10    String(syn::LitStr),
11}
12
13impl ToTokens for MemberOrString {
14    fn to_tokens(&self, tokens: &mut TokenStream) {
15        use MemberOrString::*;
16        match self {
17            Member(member) => member.to_tokens(tokens),
18            String(string) => string.to_tokens(tokens),
19        }
20    }
21}
22
23impl Parse for MemberOrString {
24    fn parse(input: ParseStream) -> syn::Result<Self> {
25        let lookahead = input.lookahead1();
26        if lookahead.peek(syn::Ident) || lookahead.peek(syn::LitInt) {
27            Ok(MemberOrString::Member(input.parse()?))
28        } else if lookahead.peek(syn::LitStr) {
29            Ok(MemberOrString::String(input.parse()?))
30        } else {
31            Err(syn::Error::new(
32                input.span(),
33                "Expected a string or a field reference.",
34            ))
35        }
36    }
37}
38
39use crate::{
40    diagnostic::{DiagnosticConcreteArgs, DiagnosticDef},
41    forward::WhichFn,
42};
43
44pub(crate) fn gen_all_variants_with(
45    variants: &[DiagnosticDef],
46    which_fn: WhichFn,
47    mut f: impl FnMut(&syn::Ident, &syn::Fields, &DiagnosticConcreteArgs) -> Option<TokenStream>,
48) -> Option<TokenStream> {
49    let pairs = variants
50        .iter()
51        .filter_map(|def| {
52            def.args
53                .forward_or_override_enum(&def.ident, which_fn, |concrete| {
54                    f(&def.ident, &def.fields, concrete)
55                })
56        })
57        .collect::<Vec<_>>();
58    if pairs.is_empty() {
59        return None;
60    }
61    let signature = which_fn.signature();
62    let catchall = which_fn.catchall_arm();
63    Some(quote! {
64        #signature {
65            #[allow(unused_variables, deprecated)]
66            match self {
67                #(#pairs)*
68                #catchall
69            }
70        }
71    })
72}
73
74use crate::fmt::Display;
75use std::collections::HashSet;
76
77pub(crate) fn gen_unused_pat(fields: &syn::Fields) -> TokenStream {
78    match fields {
79        syn::Fields::Named(_) => quote! { { .. } },
80        syn::Fields::Unnamed(_) => quote! { ( .. ) },
81        syn::Fields::Unit => quote! {},
82    }
83}
84
85/// Goes in the slot `let Self #pat = self;` or `match self { Self #pat => ...
86/// }`.
87fn gen_fields_pat(fields: &syn::Fields) -> TokenStream {
88    let member_idents = fields.iter().enumerate().map(|(i, field)| {
89        field
90            .ident
91            .as_ref()
92            .cloned()
93            .unwrap_or_else(|| format_ident!("_{}", i))
94    });
95    match fields {
96        syn::Fields::Named(_) => quote! {
97            { #(#member_idents),* }
98        },
99        syn::Fields::Unnamed(_) => quote! {
100            ( #(#member_idents),* )
101        },
102        syn::Fields::Unit => quote! {},
103    }
104}
105
106/// The returned tokens go in the slot `let Self #pat = self;` or `match self {
107/// Self #pat => ... }`. The members can be passed to
108/// `Display::expand_shorthand[_cloned]`.
109pub(crate) fn display_pat_members(fields: &syn::Fields) -> (TokenStream, HashSet<syn::Member>) {
110    let pat = gen_fields_pat(fields);
111    let members: HashSet<syn::Member> = fields
112        .iter()
113        .enumerate()
114        .map(|(i, field)| {
115            if let Some(ident) = field.ident.as_ref().cloned() {
116                syn::Member::Named(ident)
117            } else {
118                syn::Member::Unnamed(syn::Index {
119                    index: i as u32,
120                    span: field.span(),
121                })
122            }
123        })
124        .collect();
125    (pat, members)
126}
127
128impl Display {
129    /// Returns `(fmt, args)` which must be passed to some kind of format macro
130    /// without tokens in between, i.e. `format!(#fmt #args)`.
131    pub(crate) fn expand_shorthand_cloned(
132        &self,
133        members: &HashSet<syn::Member>,
134    ) -> (syn::LitStr, TokenStream) {
135        let mut display = self.clone();
136        display.expand_shorthand(members);
137        let Display { fmt, args, .. } = display;
138        (fmt, args)
139    }
140}