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
extern crate proc_macro;
use proc_macro::TokenStream;
use quote::{quote, ToTokens};
use syn::{parse_macro_input, DeriveInput};

#[proc_macro_derive(MutView)]
pub fn derive(input: TokenStream) -> TokenStream {
    let ast = parse_macro_input!(input as DeriveInput);
    let struct_name = &ast.ident;
    let fields = if let syn::Data::Struct(syn::DataStruct {
        fields: syn::Fields::Named(syn::FieldsNamed { ref named, .. }),
        ..
    }) = ast.data
    {
        named
    } else {
        panic!("Macro must be applied to struct")
    };

    let new_fields = fields.iter().map(|f| {
        let name = &f.ident;
        let ty = &f.ty;
        let ty_string = ty.to_token_stream().to_string();

        //TODO: Match using regex
        let converted_type = |ty_string: &str| {
            if ty_string.starts_with("ByteView<") {
                return quote! {let (#name, buf) = ByteView::mut_view(buf)};
            } else if ty_string.starts_with("MulByteView<") {
                return quote! {let (#name, buf) = MulByteView::mut_view(buf)};
            } else if ty_string.starts_with("ArrayView<") {
                return quote! {let (#name, buf) = ArrayView::mut_view(buf)};
            } else if ty_string.starts_with("Option") {
                return quote! {let #name = <#ty as ::core::default::Default>::default()};
            } else {
                return quote! {let (#name, buf) = <#ty>::mut_view(buf)};
            }
        };

        converted_type(&ty_string)
    });

    let field_names = fields.iter().map(|f| {
        let name = &f.ident;

        quote! {#name}
    });

    let expanded = quote! {
        impl<'a> #struct_name<'a> {
            pub fn mut_view(buf: &'a mut [u8]) -> (Self, &'a mut [u8]){
                    #(#new_fields;)*

                    (Self {
                       #(#field_names,)*
                    }, buf)
            }
        }
    };

    expanded.into()
}