Skip to main content

bindata_derive/
lib.rs

1mod repr;
2
3use proc_macro::TokenStream;
4use repr::Repr;
5use std::usize;
6use syn::{Data, DataEnum, DataStruct, DeriveInput, Expr, Fields, Index};
7
8fn encode_struct(data: DataStruct) -> proc_macro2::TokenStream {
9    match data.fields {
10        Fields::Unnamed(fields) => {
11            let tys = fields.unnamed.iter().map(|field| &field.ty);
12            let counter = (0..usize::MAX).map(Index::from);
13
14            quote::quote! {
15                #(<#tys as ::bindata::Encode>::encode(self.#counter, writer));*
16            }
17        }
18        Fields::Named(fields) => {
19            let tys = fields.named.iter().map(|field| &field.ty);
20            let names = fields
21                .named
22                .iter()
23                .map(|field| field.ident.as_ref().unwrap());
24
25            quote::quote! {
26                #(<#tys as ::bindata::Encode>::encode(self.#names, writer));*
27            }
28        }
29        Fields::Unit => quote::quote! {},
30    }
31}
32
33fn encode_enum(repr: Repr, data: DataEnum) -> proc_macro2::TokenStream {
34    for variant in &data.variants {
35        match variant.fields {
36            Fields::Unit => {}
37            _ => panic!("enum fields must not contain any data"),
38        }
39    }
40
41    let names = data.variants.iter().map(|variant| &variant.ident);
42    let discriminants = enum_discriminants(&data);
43
44    quote::quote! {
45        match self {
46            #(Self::#names => writer.write::<#repr>(#discriminants),)*
47        }
48    }
49}
50
51fn enum_discriminants(data: &DataEnum) -> impl Iterator<Item = &Expr> {
52    data.variants
53        .iter()
54        .map(|variant| match variant.discriminant.as_ref() {
55            Some(discriminant) => &discriminant.1,
56            None => panic!("enums must have explicit discriminants"),
57        })
58}
59
60#[proc_macro_derive(Encode)]
61pub fn derive_encode(input: TokenStream) -> TokenStream {
62    let input = syn::parse_macro_input!(input as DeriveInput);
63    let body = match input.data {
64        Data::Struct(data) => encode_struct(data),
65        Data::Enum(data) => {
66            let repr = match Repr::parse(&input.attrs) {
67                Ok(repr) => repr,
68                Err(err) => panic!("failed to parse repr: {}", err),
69            };
70
71            encode_enum(repr, data)
72        }
73        Data::Union(_) => panic!("only structs and enums can #[derive(Encode)]"),
74    };
75
76    let name = input.ident;
77    let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
78    (quote::quote! {
79        impl #impl_generics ::bindata::Encode for #name #ty_generics #where_clause {
80            fn encode(self, writer: &mut ::bindata::Writer) {
81                #body
82            }
83        }
84    })
85    .into()
86}
87
88fn decode_struct(data: DataStruct) -> proc_macro2::TokenStream {
89    match data.fields {
90        Fields::Unnamed(fields) => {
91            let tys = fields.unnamed.iter().map(|field| &field.ty);
92
93            quote::quote! {
94                Ok(Self(#(<#tys as ::bindata::Decode>::decode(reader)?),*))
95            }
96        }
97        Fields::Named(fields) => {
98            let names = fields
99                .named
100                .iter()
101                .map(|field| field.ident.as_ref().unwrap());
102            let tys = fields.named.iter().map(|field| &field.ty);
103
104            quote::quote! {
105                Ok(Self {
106                    #(#names: <#tys as ::bindata::Decode>::decode(reader)?),*
107                })
108            }
109        }
110        Fields::Unit => quote::quote! { Ok(Self) },
111    }
112}
113
114fn decode_enum(repr: Repr, data: DataEnum) -> proc_macro2::TokenStream {
115    let names = data.variants.iter().map(|variant| &variant.ident);
116    let discriminants = enum_discriminants(&data);
117
118    quote::quote! {
119        let value = reader.read::<#repr>()?;
120
121        #(if value == #discriminants {
122            return Ok(Self::#names);
123        })*
124
125        Err(::bindata::Error::InvalidVariant)
126    }
127}
128
129#[proc_macro_derive(Decode)]
130pub fn derive_decode(input: TokenStream) -> TokenStream {
131    let input = syn::parse_macro_input!(input as DeriveInput);
132    let body = match input.data {
133        Data::Struct(data) => decode_struct(data),
134        Data::Enum(data) => {
135            let repr = match Repr::parse(&input.attrs) {
136                Ok(repr) => repr,
137                Err(err) => panic!("failed to parse repr: {}", err),
138            };
139
140            decode_enum(repr, data)
141        }
142        Data::Union(_) => panic!("only structs and enums can #[derive(Encode)]"),
143    };
144
145    let name = input.ident;
146    let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
147    (quote::quote! {
148        impl #impl_generics ::bindata::Decode for #name #ty_generics #where_clause {
149            fn decode(reader: &mut ::bindata::Reader) -> Result<Self, ::bindata::Error> {
150                #body
151            }
152        }
153    })
154    .into()
155}
156
157fn encoded_size_struct(data: DataStruct) -> proc_macro2::TokenStream {
158    let fields = match data.fields {
159        Fields::Unnamed(fields) => fields.unnamed,
160        Fields::Named(fields) => fields.named,
161        Fields::Unit => return quote::quote! { 0 },
162    };
163
164    let tys = fields.iter().map(|field| &field.ty);
165    quote::quote! {
166        0 #(+ <#tys as ::bindata::EncodedSize>::SIZE)*
167    }
168}
169
170fn encoded_size_enum(repr: Repr) -> proc_macro2::TokenStream {
171    quote::quote! { <#repr as ::bindata::EncodedSize>::SIZE  }
172}
173
174#[proc_macro_derive(EncodedSize)]
175pub fn derive_encoded_size(input: TokenStream) -> TokenStream {
176    let input = syn::parse_macro_input!(input as DeriveInput);
177    let body = match input.data {
178        Data::Struct(data) => encoded_size_struct(data),
179        Data::Enum(_) => {
180            let repr = match Repr::parse(&input.attrs) {
181                Ok(repr) => repr,
182                Err(err) => panic!("failed to parse repr: {}", err),
183            };
184
185            encoded_size_enum(repr)
186        }
187        Data::Union(_) => panic!("only structs and enums can #[derive(EncodedSize)]"),
188    };
189
190    let name = input.ident;
191    let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
192    (quote::quote! {
193        impl #impl_generics ::bindata::EncodedSize for #name #ty_generics #where_clause {
194            const SIZE: usize = #body;
195        }
196    })
197    .into()
198}