1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
use crate::internal::get_root_path;
use proc_macro2::TokenStream as TokenStream2;
use quote::{quote, quote_spanned, ToTokens};
use syn::{
parse::{Parse, ParseStream, Result as ParseResult},
parse_quote,
spanned::Spanned,
Data, DeriveInput, Fields, GenericParam, Generics, Ident, Index, Path,
};
pub struct DeriveWrite {
ident: Ident,
generics: Generics,
data: Data,
root_path: Path,
}
impl Parse for DeriveWrite {
fn parse(input: ParseStream) -> ParseResult<Self> {
let DeriveInput {
attrs,
ident,
mut generics,
data,
..
} = input.parse()?;
let root_path = get_root_path(&attrs);
for param in &mut generics.params {
if let GenericParam::Type(ref mut type_param) = *param {
type_param.bounds.push(parse_quote!(#root_path::Write));
}
}
Ok(Self {
ident,
generics,
data,
root_path,
})
}
}
impl ToTokens for DeriveWrite {
fn to_tokens(&self, tokens: &mut TokenStream2) {
let root = &self.root_path;
let name = &self.ident;
let (impl_generics, ty_generics, where_clause) =
self.generics.split_for_impl();
let call_site = ::proc_macro2::Span::call_site();
let var = quote!(self);
let writes = match &self.data {
Data::Struct(ref data) => match data.fields {
Fields::Named(ref fields) => {
let recurse = fields.named.iter().map(|f| {
let name = &f.ident;
let access = quote_spanned!(call_site => #var.#name);
quote_spanned! { f.span() =>
#root::Write::write(&#access, bytes, pos)?;
}
});
quote! {
#(#recurse)*
Ok(())
}
}
Fields::Unnamed(ref fields) => {
let recurse =
fields.unnamed.iter().enumerate().map(|(i, f)| {
let index = Index {
index: i as u32,
span: call_site,
};
let access =
quote_spanned!(call_site => #var.#index);
quote_spanned! { f.span() =>
#root::Write::write(&#access, bytes, pos)?;
}
});
quote! {
#(#recurse)*
Ok(())
}
}
Fields::Unit => {
quote! {
Ok(())
}
}
},
Data::Enum(_) | Data::Union(_) => unimplemented!(),
};
let expanded = quote! {
#[automatically_derived]
#[allow(unused_qualifications)]
impl #impl_generics #root::Write for #name #ty_generics #where_clause {
#[inline]
fn write(&self, bytes: &mut [u8], pos: &mut usize) -> Result<(), #root::WriteError> {
#writes
}
}
};
expanded.to_tokens(tokens);
}
}