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
#![allow(unused_imports, unused_macros)]
extern crate proc_macro;

use proc_macro::TokenStream;
use quote::{quote, quote_spanned, format_ident, ToTokens};
use syn::{
    spanned::Spanned,
    parse_macro_input,
    DeriveInput
};

mod codegen;
mod meta_attrs;
mod binread_endian;
mod compiler_error;

use codegen::sanitization::*;
use proc_macro2::{TokenStream as TokenStream2, Span};
use compiler_error::{CompileError, SpanError};

fn generate_derive(input: DeriveInput, code: codegen::GeneratedCode) -> TokenStream {
    let codegen::GeneratedCode {
        read_opt_impl, after_parse_impl, arg_type
    } = code;

    let name = input.ident;
    let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
    quote!(
        #[allow(warnings)]
        impl #impl_generics #TRAIT_NAME for #name #ty_generics #where_clause {
            type Args = #arg_type;

            fn read_options<R: #READ_TRAIT + #SEEK_TRAIT>
                (#READER: &mut R, #OPT: &#OPTIONS, #ARGS: Self::Args)
                -> #BIN_RESULT<Self>
            {
                #read_opt_impl
            }

            fn after_parse<R: #READ_TRAIT + #SEEK_TRAIT> (&mut self, #READER: &mut R,
                #OPT : &#OPTIONS, #ARGS : Self::Args) 
                -> #BIN_RESULT<()>
            {
                #after_parse_impl
            }
        }
    ).into()
}

#[proc_macro_derive(BinRead, attributes(binread, br))]
pub fn derive_binread(input: TokenStream) -> TokenStream {
    let input: DeriveInput = parse_macro_input!(input as DeriveInput);

    match codegen::generate(&input) {
        Ok(code) => {
            generate_derive(input, code)
        }
        Err(err) => {
            let error = match err {
                CompileError::SpanError(span_err) => {
                    let SpanError (span, error) = span_err;
                    let error: &str = &error;
                    quote_spanned!{ span =>
                        compile_error!(#error)
                    }
                }
                CompileError::Syn(syn_err) => syn_err.to_compile_error()
                
            };
            generate_derive(input, codegen::GeneratedCode::new(
                quote!(todo!()),
                error,
                quote!(())
            ))
        }
    }
}