open_metrics_client_derive_text_encode/
lib.rs

1extern crate proc_macro;
2
3use proc_macro::TokenStream;
4use proc_macro2::TokenStream as TokenStream2;
5use quote::quote;
6use syn::DeriveInput;
7
8#[proc_macro_derive(Encode)]
9pub fn derive_encode(input: TokenStream) -> TokenStream {
10    let ast: DeriveInput = syn::parse(input).unwrap();
11    let name = &ast.ident;
12
13    let body = match ast.data {
14        syn::Data::Struct(s) => match s.fields {
15            syn::Fields::Named(syn::FieldsNamed { named, .. }) => named
16                .into_iter()
17                .enumerate()
18                .map(|(i, f)| {
19                    let ident = f.ident.unwrap();
20                    let ident_string = KEYWORD_IDENTIFIERS
21                        .iter()
22                        .find(|pair| ident == pair.1)
23                        .map(|pair| pair.0.to_string())
24                        .unwrap_or_else(|| ident.to_string());
25
26                    let maybe_comma = if i == 0 {
27                        TokenStream2::default()
28                    } else {
29                        quote! { writer.write_all(b",")?; }
30                    };
31                    quote! {
32                        #maybe_comma
33                        writer.write_all(concat!(#ident_string, "=\"").as_bytes())?;
34                        open_metrics_client::encoding::text::Encode::encode(&self.#ident, writer)?;
35                        writer.write_all(b"\"")?;
36                    }
37                })
38                .collect(),
39            syn::Fields::Unnamed(_) => {
40                panic!("Can not derive Encode for struct with unnamed fields.")
41            }
42            syn::Fields::Unit => panic!("Can not derive Encode for struct with unit field."),
43        },
44        syn::Data::Enum(syn::DataEnum { variants, .. }) => {
45            let match_arms: TokenStream2 = variants
46                .into_iter()
47                .map(|v| {
48                    let ident = v.ident;
49                    quote! {
50                        #name::#ident => writer.write_all(stringify!(#ident).as_bytes())?,
51                    }
52                })
53                .collect();
54
55            quote! {
56                match self {
57                    #match_arms
58                }
59            }
60        }
61        syn::Data::Union(_) => panic!("Can not derive Encode for union."),
62    };
63
64    let gen = quote! {
65        impl open_metrics_client::encoding::text::Encode for #name {
66            fn encode(&self, writer: &mut dyn std::io::Write) -> std::result::Result<(), std::io::Error> {
67                #body
68
69                Ok(())
70            }
71        }
72    };
73    gen.into()
74}
75
76// Copied from https://github.com/djc/askama (MIT and APACHE licensed) and
77// modified.
78static KEYWORD_IDENTIFIERS: [(&str, &str); 48] = [
79    ("as", "r#as"),
80    ("break", "r#break"),
81    ("const", "r#const"),
82    ("continue", "r#continue"),
83    ("crate", "r#crate"),
84    ("else", "r#else"),
85    ("enum", "r#enum"),
86    ("extern", "r#extern"),
87    ("false", "r#false"),
88    ("fn", "r#fn"),
89    ("for", "r#for"),
90    ("if", "r#if"),
91    ("impl", "r#impl"),
92    ("in", "r#in"),
93    ("let", "r#let"),
94    ("loop", "r#loop"),
95    ("match", "r#match"),
96    ("mod", "r#mod"),
97    ("move", "r#move"),
98    ("mut", "r#mut"),
99    ("pub", "r#pub"),
100    ("ref", "r#ref"),
101    ("return", "r#return"),
102    ("static", "r#static"),
103    ("struct", "r#struct"),
104    ("trait", "r#trait"),
105    ("true", "r#true"),
106    ("type", "r#type"),
107    ("unsafe", "r#unsafe"),
108    ("use", "r#use"),
109    ("where", "r#where"),
110    ("while", "r#while"),
111    ("async", "r#async"),
112    ("await", "r#await"),
113    ("dyn", "r#dyn"),
114    ("abstract", "r#abstract"),
115    ("become", "r#become"),
116    ("box", "r#box"),
117    ("do", "r#do"),
118    ("final", "r#final"),
119    ("macro", "r#macro"),
120    ("override", "r#override"),
121    ("priv", "r#priv"),
122    ("typeof", "r#typeof"),
123    ("unsized", "r#unsized"),
124    ("virtual", "r#virtual"),
125    ("yield", "r#yield"),
126    ("try", "r#try"),
127];