1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
use std::collections::HashSet;

use quote::quote;
use syn::{punctuated::Punctuated, token::Comma, GenericParam, Path, Type};

use crate::common::r#type::{dereference, find_idents_in_type};

#[inline]
pub(crate) fn create_debug_map_builder() -> proc_macro2::TokenStream {
    quote!(
        struct RawString(&'static str);

        impl ::core::fmt::Debug for RawString {
            #[inline]
            fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
                f.write_str(self.0)
            }
        }

        let mut builder = f.debug_map();
    )
}

#[inline]
pub(crate) fn create_format_arg(
    params: &Punctuated<GenericParam, Comma>,
    ty: &Type,
    format_method: &Path,
    field: proc_macro2::TokenStream,
) -> proc_macro2::TokenStream {
    let ty = dereference(ty);

    let mut idents = HashSet::new();
    find_idents_in_type(&mut idents, ty, Some((true, true, false)));

    // simply support one level generics (without considering bounds that use other generics)
    let mut filtered_params: Punctuated<GenericParam, Comma> = Punctuated::new();

    for param in params.iter() {
        if let GenericParam::Type(ty) = param {
            let ident = &ty.ident;

            if idents.contains(ident) {
                filtered_params.push(param.clone());
            }
        }
    }

    quote!(
        let arg = {
            struct MyDebug<'a, #filtered_params>(&'a #ty);

            impl<'a, #filtered_params> ::core::fmt::Debug for MyDebug<'a, #filtered_params> {
                #[inline]
                fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
                    #format_method(self.0, f)
                }
            }

            MyDebug(#field)
        };
    )
}