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
56fn 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#[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
116fn 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}