tetsy_codec_derive/
lib.rs1extern crate proc_macro;
18extern crate proc_macro2;
19
20#[macro_use]
21extern crate syn;
22
23#[macro_use]
24extern crate quote;
25
26use proc_macro::TokenStream;
27use proc_macro2::Span;
28use syn::{DeriveInput, Generics, GenericParam, Ident};
29
30mod decode;
31mod encode;
32mod utils;
33
34const ENCODE_ERR: &str = "derive(Encode) failed";
35
36#[proc_macro_derive(Encode, attributes(codec))]
37pub fn encode_derive(input: TokenStream) -> TokenStream {
38 let input: DeriveInput = syn::parse(input).expect(ENCODE_ERR);
39 let name = &input.ident;
40
41 let generics = add_trait_bounds(input.generics, parse_quote!(_tetsy_codec::Encode));
42 let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
43
44 let self_ = quote!(self);
45 let dest_ = quote!(dest);
46 let encoding = encode::quote(&input.data, name, &self_, &dest_);
47
48 let impl_block = quote! {
49 impl #impl_generics _tetsy_codec::Encode for #name #ty_generics #where_clause {
50 fn encode_to<EncOut: _tetsy_codec::Output>(&#self_, #dest_: &mut EncOut) {
51 #encoding
52 }
53 }
54 };
55
56 let mut new_name = "_IMPL_ENCODE_FOR_".to_string();
57 new_name.push_str(name.to_string().trim_start_matches("r#"));
58 let dummy_const = Ident::new(&new_name, Span::call_site());
59
60 let generated = quote! {
61 #[allow(non_upper_case_globals, unused_attributes, unused_qualifications)]
62 const #dummy_const: () = {
63 #[allow(unknown_lints)]
64 #[cfg_attr(feature = "cargo-clippy", allow(useless_attribute))]
65 #[allow(rust_2018_idioms)]
66 extern crate tetsy_codec as _tetsy_codec;
67 #impl_block
68 };
69 };
70
71 generated.into()
72}
73
74#[proc_macro_derive(Decode, attributes(codec))]
75pub fn decode_derive(input: TokenStream) -> TokenStream {
76 let input: DeriveInput = syn::parse(input).expect(ENCODE_ERR);
77 let name = &input.ident;
78
79 let generics = add_trait_bounds(input.generics, parse_quote!(_tetsy_codec::Decode));
80 let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
81
82 let input_ = quote!(input);
83 let decoding = decode::quote(&input.data, name, &input_);
84
85 let impl_block = quote! {
86 impl #impl_generics _tetsy_codec::Decode for #name #ty_generics #where_clause {
87 fn decode<DecIn: _tetsy_codec::Input>(#input_: &mut DecIn) -> Option<Self> {
88 #decoding
89 }
90 }
91 };
92
93 let mut new_name = "_IMPL_DECODE_FOR_".to_string();
94 new_name.push_str(name.to_string().trim_start_matches("r#"));
95 let dummy_const = Ident::new(&new_name, Span::call_site());
96
97 let generated = quote! {
98 #[allow(non_upper_case_globals, unused_attributes, unused_qualifications)]
99 const #dummy_const: () = {
100 #[allow(unknown_lints)]
101 #[cfg_attr(feature = "cargo-clippy", allow(useless_attribute))]
102 #[allow(rust_2018_idioms)]
103 extern crate tetsy_codec as _tetsy_codec;
104 #impl_block
105 };
106 };
107
108 generated.into()
109}
110
111fn add_trait_bounds(mut generics: Generics, bounds: syn::TypeParamBound) -> Generics {
112 for param in &mut generics.params {
113 if let GenericParam::Type(ref mut type_param) = *param {
114 type_param.bounds.push(bounds.clone());
115 }
116 }
117 generics
118}