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}