macron_impl_display/
lib.rs1use proc_macro::TokenStream;
4use quote::quote;
5
6#[proc_macro_derive(Display, attributes(display))]
8pub fn impl_display(input: TokenStream) -> TokenStream {
9 let syn::DeriveInput { ident, data, attrs, .. } = syn::parse_macro_input!(input as syn::DeriveInput);
10
11 match data {
12 syn::Data::Struct(st) => {
14 let output = if let Some(fmt) = read_attr_value(&attrs) {
16 let args = st.fields
17 .iter()
18 .map(|field| field.ident.clone().unwrap())
19 .filter(|ident| {
20 let name = ident.to_string();
21 fmt.contains(&format!("{{{name}}}")) || fmt.contains(&format!("{{{name}:"))
22 });
23
24 quote! { write!(f, #fmt, #(#args = &self.#args,)*) }
25 }
26 else {
28 quote! { write!(f, stringify!(#ident)) }
29 };
30
31 quote! {
32 impl ::std::fmt::Display for #ident {
33 fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
34 #output
35 }
36 }
37 }.into()
38 },
39
40 syn::Data::Enum(en) => {
42 if en.variants.is_empty() {
43 return quote! {
44 impl ::std::fmt::Display for #ident {
45 fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
46 write!(f, "")
47 }
48 }
49 }.into();
50 }
51
52 let vars = en.variants
53 .iter()
54 .map(|syn::Variant { ident: var_ident, fields, attrs, .. }| {
55 if let Some(mut fmt) = read_attr_value(&attrs) {
57 match fields {
58 syn::Fields::Named(_) => {
60 let args = fields
61 .iter()
62 .map(|field| field.ident.clone().unwrap())
63 .filter(|ident| {
64 let name = ident.to_string();
65
66 fmt.contains(&format!("{{{name}}}"))
67 || fmt.contains(&format!("{{{name}:"))
68 });
69
70 quote! { Self::#var_ident { #(#args,)* .. } => write!(f, #fmt) }
71 },
72
73 syn::Fields::Unnamed(_) => {
75 let args = (0..fields.len())
76 .into_iter()
77 .map(|i| {
78 let arg = if fmt.contains(&format!("{{{i}}}"))
79 || fmt.contains(&format!("{{{i}:")) {
80 format!("arg{i}")
81 } else {
82 fmt.push_str(&format!("{{{i}}}"));
83 format!("_")
84 };
85
86 proc_macro2::Ident::new(&arg, proc_macro2::Span::call_site())
87 })
88 .collect::<Vec<_>>();
89
90 let vals = args.clone()
91 .into_iter()
92 .map(|arg|
93 if arg != "_" {
94 quote! { #arg }
95 } else {
96 quote! { "" }
97 }
98 );
99
100 quote! { Self::#var_ident(#(#args,)*) => write!(f, #fmt, #(#vals,)*) }
101 },
102
103 _ => quote! { Self::#var_ident => write!(f, #fmt) }
104 }
105 }
106 else {
108 quote! { Self::#var_ident => write!(f, stringify!(#var_ident)) }
109 }
110 });
111
112 quote! {
113 impl ::std::fmt::Display for #ident {
114 fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
115 match &self {
116 #(
117 #vars,
118 )*
119 }
120 }
121 }
122 }.into()
123 },
124
125 _ => panic!("Expected a 'struct' or 'enum'")
126 }
127}
128
129fn read_attr_value(attrs: &[syn::Attribute]) -> Option<String>{
131 attrs
132 .iter()
133 .find(|attr| attr.path().is_ident("display"))
134 .map(|attr|
135 match &attr.meta {
136 syn::Meta::NameValue(meta) => {
137 if let syn::Expr::Lit(lit) = &meta.value {
138 if let syn::Lit::Str(s) = &lit.lit {
139 return s.value();
140 }
141 }
142
143 panic!("Expected the attribute format like this '#[display = \"a text..\"]'");
144 },
145
146 _ => panic!("Expected the attribute format like this '#[display = \"a text..\"]'")
147 }
148 )
149}