bin_layout_derive/
lib.rs

1use proc_macro::TokenStream;
2use quote::quote;
3use std::str::FromStr;
4use syn::punctuated::Punctuated;
5use syn::*;
6
7use quote::__private::{Span, TokenStream as TokenS};
8
9#[proc_macro_derive(Encoder)]
10pub fn encoder(input: TokenStream) -> TokenStream {
11    let DeriveInput {
12        data,
13        ident,
14        mut generics,
15        ..
16    } = parse_macro_input!(input);
17
18    trait_bounds(&mut generics, parse_quote!(bin_layout::Encoder));
19    let (_, ty_generics, where_clause) = generics.split_for_impl();
20    let body = {
21        let mut body = String::from("use bin_layout::Encoder as _E_;");
22        let mut write_encoder = |is_ref, ident: String| {
23            body.push_str("_E_::encoder(");
24            body.push_str(if is_ref { "self." } else { "&self." });
25            body.push_str(&ident);
26            body.push_str(",c)?;");
27        };
28        match data {
29            Data::Struct(data_struct) => match data_struct.fields {
30                Fields::Named(fields) => {
31                    for field in fields.named {
32                        let is_ref = matches!(field.ty, Type::Reference(_));
33                        write_encoder(is_ref, field.ident.unwrap().to_string());
34                    }
35                }
36                Fields::Unnamed(fields) => {
37                    for (i, field) in fields.unnamed.into_iter().enumerate() {
38                        let is_ref = matches!(field.ty, Type::Reference(_));
39                        write_encoder(is_ref, i.to_string());
40                    }
41                }
42                Fields::Unit => {}
43            },
44            _ => panic!("Default `Encoder` implementation for `enum` not yet stabilized"),
45        };
46        body.push_str("Ok(())");
47        TokenS::from_str(&body).unwrap()
48    };
49    TokenStream::from(quote! {
50        impl #generics bin_layout::Encoder for #ident #ty_generics #where_clause {
51            fn encoder(&self, c: &mut impl std::io::Write) -> std::io::Result<()> { #body }
52        }
53    })
54}
55
56/// Add a bound `T: Encoder` to every type parameter of `T`.
57fn trait_bounds(generics: &mut Generics, bound: TypeParamBound) {
58    for param in &mut generics.params {
59        if let GenericParam::Type(type_param) = param {
60            type_param.bounds.push(bound.clone());
61        }
62    }
63}
64
65// -------------------------------------------------------------------------------
66
67#[proc_macro_derive(Decoder)]
68pub fn decoder(input: TokenStream) -> TokenStream {
69    let DeriveInput {
70        data,
71        ident,
72        generics,
73        ..
74    } = parse_macro_input!(input);
75
76    let (lt, ig) = decoder_trait_bounds(&generics);
77    let (_, ty_generics, where_clause) = generics.split_for_impl();
78    let body = {
79        let mut body = String::from("use bin_layout::Decoder as _D_; Ok(Self");
80        match data {
81            Data::Struct(data_struct) => match data_struct.fields {
82                Fields::Named(fields) => {
83                    body.push('{');
84                    for field in fields.named {
85                        body.push_str(&field.ident.unwrap().to_string());
86                        body.push(':');
87                        body.push_str("_D_::decoder(c)?,");
88                    }
89                    body.push('}');
90                }
91                Fields::Unnamed(fields) => {
92                    body.push('(');
93                    for _ in fields.unnamed {
94                        body.push_str("_D_::decoder(c)?,");
95                    }
96                    body.push(')');
97                }
98                Fields::Unit => {}
99            },
100            _ => panic!("Default `Decoder<'_>` implementation for `enum` not yet stabilized"),
101        };
102        body.push(')');
103        TokenS::from_str(&body).unwrap()
104    };
105    TokenStream::from(quote! {
106        impl <#lt, #ig> bin_layout::Decoder<'_de_> for #ident #ty_generics
107        #where_clause
108        {
109            fn decoder(c: &mut &'_de_ [u8]) -> std::result::Result<Self, std::boxed::Box<dyn std::error::Error + std::marker::Send + std::marker::Sync>> {
110                #body
111            }
112        }
113    })
114}
115
116/// Add a bound `T: Decoder<'de>` to every type parameter of `T`.
117fn decoder_trait_bounds(g: &Generics) -> (LifetimeDef, Punctuated<GenericParam, token::Comma>) {
118    let mut de_lifetime = LifetimeDef::new(Lifetime::new("'_de_", Span::call_site()));
119    let mut params = g.params.clone();
120    for param in &mut params {
121        match param {
122            GenericParam::Type(ty) => ty.bounds.push(parse_quote!(bin_layout::Decoder<'_de_>)),
123            GenericParam::Lifetime(lt) => de_lifetime.bounds.push(lt.lifetime.clone()),
124            _ => {}
125        }
126    }
127    (de_lifetime, params)
128}