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
//! horrible but necessary all i have to say
extern crate proc_macro;

use proc_macro::TokenStream;
use syn;
use std::str::FromStr;
use syn::export::ToTokens;

#[proc_macro_derive(Marshall, attributes(marshall))]
pub fn marshall_derive(input: TokenStream) -> TokenStream {
    // see so far so good
    let ast = syn::parse(input).unwrap();
    impl_marshall(&ast)
}

fn impl_marshall(ast: &syn::DeriveInput) -> TokenStream {
    let name = &ast.ident;
    let (marshall, unmarshall) = marshall_content(ast);
    // oh god...
    let implementation = stringify! (
        impl bytebuff::Marshall<#name> for #name {
            fn marshall(&self, buff: &mut bytebuff::ByteBuff) {
                    #marshall
            }
            fn unmarshall(buff: &mut bytebuff::ByteBuff) -> Result<#name, bytebuff::ReadError> {
                Ok(Self {
                    #unmarshall
                })
            }
        }

    );

    TokenStream::from_str(implementation
        .replace("# name", name.to_string().as_str()) // the space between hashtag and name is absolute top of the cake...
        .replace("# marshall", marshall.as_str())
        .replace("# unmarshall", unmarshall.as_str())
        .as_str()
    ).unwrap()
}

fn marshall_content(ast: &syn::DeriveInput) -> (String, String) {
    let mut marshall = String::new();
    let mut unmarshall = String::new();

    match &ast.data {
        syn::Data::Struct(data) => {
            'main: for field in data.fields.iter() {
                let name = &field.ident.as_ref().unwrap().to_string();
                let ty = &field.ty.to_token_stream().to_string();
                for atr in field.attrs.iter() {
                    if atr.path.is_ident("marshall") {
                        match atr.tokens.to_string().as_str() {
                            "(ignore)" => { // just look at these brackets... just look at them
                                unmarshall += stringify!(#: Default::default(),).replace("#", name).as_str();
                                continue 'main;
                            }
                            "(nested)" => {
                                marshall += stringify!(bytebuff::Marshall::marshall(&self.#, buff);).replace("#", name).as_str();
                                //have you ewer done this kind of shit? EVER?
                                unmarshall += stringify!(#: <@ as bytebuff::Marshall<@>>::unmarshall(buff)?,).replace("#", name).replace("@", ty).as_str();
                                continue 'main;
                            }
                            _ => ()
                        }
                    }
                }
                marshall += stringify!(bytebuff::Interface::write(buff, self.#.clone());).replace("#", name).as_str();
                unmarshall += stringify!(#: bytebuff::Interface::read(buff)?,).replace("#", name).as_str();
            }
        }
        _ => ()
    }

    (marshall, unmarshall)
}

// and that all because quote! replaces "unknown" tokens with ()