ender_derive/
lib.rs

1use proc_macro::TokenStream as TokenStream1;
2
3use proc_macro2::{Ident, Span};
4use quote::quote;
5use syn::{parse_macro_input, parse_quote, DeriveInput, GenericParam};
6
7use crate::ctxt::{Ctxt, Target};
8
9mod ctxt;
10mod enums;
11mod flags;
12mod generator;
13mod lifetime;
14mod parse;
15
16static ENDER: &str = "ender";
17
18/// Emulates the $crate available in regular macros
19fn dollar_crate(name: &str) -> Ident {
20    let crate_name = std::env::var("CARGO_PKG_NAME").expect("Can't obtain current crate name");
21    Ident::new(
22        &if crate_name == name {
23            "crate".to_owned()
24        } else {
25            name.replace("-", "_")
26        },
27        Span::call_site(),
28    )
29}
30
31#[proc_macro_derive(Encode, attributes(ender))]
32pub fn encode(input: TokenStream1) -> TokenStream1 {
33    let input = parse_macro_input!(input as DeriveInput);
34    let ctxt = match Ctxt::parse_from(&input, Target::Encode) {
35        Ok(ctxt) => ctxt,
36        Err(err) => return TokenStream1::from(err.to_compile_error()),
37    };
38
39    let ref encoder_generic = ctxt.encoder_generic;
40    let ref crate_name = ctxt.flags.crate_name;
41    let type_param = if ctxt.requires_seeking_impl() {
42        parse_quote!(#encoder_generic: #crate_name::io::Write + #crate_name::io::Seek)
43    } else {
44        parse_quote!(#encoder_generic: #crate_name::io::Write)
45    };
46
47    // Inject the decoder's generic parameter in the `impl` generics
48    let mut generics = ctxt.generics.clone();
49    generics.params.push(GenericParam::Type(type_param));
50
51    // Impl generics use injected generics
52    let (impl_generics, _, _) = generics.split_for_impl();
53    // Ty and where clause use the original generics
54    let (_, ty_generics, where_clause) = ctxt.generics.split_for_impl();
55    let ref item_name = ctxt.item_name;
56    let ref encoder = ctxt.encoder;
57
58    let body = match ctxt.derive() {
59        Ok(ctxt) => ctxt,
60        Err(err) => return TokenStream1::from(err.to_compile_error()),
61    };
62
63    quote!(
64        #[automatically_derived]
65        #[allow(unused)]
66        #[allow(dead_code)]
67        impl #impl_generics #crate_name::Encode<#encoder_generic> for #item_name #ty_generics #where_clause {
68            fn encode(&self, #encoder: &mut #crate_name::Encoder<#encoder_generic>) -> #crate_name::EncodingResult<()> {
69                #body
70            }
71        }
72    ).into()
73}
74
75#[proc_macro_derive(Decode, attributes(ender))]
76pub fn decode(input: TokenStream1) -> TokenStream1 {
77    let input = parse_macro_input!(input as DeriveInput);
78    let ctxt = match Ctxt::parse_from(&input, Target::Decode) {
79        Ok(ctxt) => ctxt,
80        Err(err) => return TokenStream1::from(err.to_compile_error()),
81    };
82
83    let ref encoder_generic = ctxt.encoder_generic;
84    let ref crate_name = ctxt.flags.crate_name;
85    let ref decoder_lif = ctxt.borrow_data.decoder;
86    
87    let type_param = if ctxt.requires_borrowing_impl() {
88        if ctxt.requires_seeking_impl() {
89            parse_quote!(#encoder_generic: #crate_name::io::BorrowRead<#decoder_lif> + #crate_name::io::Seek)
90        } else {
91            parse_quote!(#encoder_generic: #crate_name::io::BorrowRead<#decoder_lif>)
92        }
93    } else {
94        if ctxt.requires_seeking_impl() {
95            parse_quote!(#encoder_generic: #crate_name::io::Read + #crate_name::io::Seek)
96        } else {
97            parse_quote!(#encoder_generic: #crate_name::io::Read)
98        }
99    };
100
101    let lif = if ctxt.borrow_data.sub_lifetimes.is_empty() {
102        parse_quote!(
103            #decoder_lif
104        )
105    } else {
106        let sub_lifs = ctxt.borrow_data.sub_lifetimes.iter();
107        parse_quote!(
108            #decoder_lif: #(#sub_lifs)+*
109        )
110    };
111    
112    // Inject the decoder's generic parameter and lifetime in the `impl` generics
113    let mut generics = ctxt.generics.clone();
114    generics.params.push(GenericParam::Type(type_param));
115    if ctxt.requires_borrowing_impl() {
116        generics.params.insert(0, GenericParam::Lifetime(lif));
117    }
118    
119    // Impl generics use injected generics
120    let (impl_generics, _, _) = generics.split_for_impl();
121    // Ty and where clause use the original generics
122    let (_, ty_generics, where_clause) = ctxt.generics.split_for_impl();
123    let ref item_name = ctxt.item_name;
124    let ref encoder = ctxt.encoder;
125
126    let body = match ctxt.derive() {
127        Ok(ctxt) => ctxt,
128        Err(err) => return TokenStream1::from(err.to_compile_error()),
129    };
130
131    quote!(
132        #[automatically_derived]
133        #[allow(unused)]
134        #[allow(dead_code)]
135        impl #impl_generics #crate_name::Decode<#encoder_generic> for #item_name #ty_generics #where_clause {
136            fn decode(#encoder: &mut #crate_name::Encoder<#encoder_generic>) -> #crate_name::EncodingResult<Self> {
137                #body
138            }
139        }
140    ).into()
141}