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
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
extern crate proc_macro;

mod r#enum;
mod helper;
mod r#struct;

use crate::helper::IROHA;
use helper::MOD_PATH;
use proc_macro::TokenStream;
use proc_macro2::TokenStream as TokenStream2;
use r#enum::EnumStructure;
use r#struct::StructStructure;
use std::str::FromStr;
use syn::{parse_macro_input, Data, DeriveInput, Error, Lit, Meta, NestedMeta};

#[proc_macro_derive(ToTokens, attributes(Iroha))]
pub fn derive_to_tokens(input: TokenStream) -> TokenStream {
    let input = parse_macro_input! {input as DeriveInput};
    let mut mod_path_tokens: Result<Option<TokenStream2>, Error> = Ok(None);

    for attr in &input.attrs {
        if attr.path == IROHA {
            match attr.parse_meta() {
                Ok(Meta::List(list)) => {
                    for item in list.nested.iter() {
                        mod_path_tokens = match item {
                            NestedMeta::Meta(Meta::NameValue(mod_path)) => {
                                if mod_path.path != MOD_PATH {
                                    Err(Error::new_spanned(
                                        &attr,
                                        "Attribute `Iroha` only support argument `mod_path`",
                                    ))
                                } else {
                                    match match &mod_path.lit {
                                        Lit::Str(path_str) => Ok(path_str.value()),
                                        _ => Err(Error::new_spanned(
                                            &mod_path,
                                            "`mod_path` must be a string",
                                        )),
                                    }
                                    .map(|path| {
                                        TokenStream2::from_str(path.as_str()).map_err(|_| {
                                            Error::new_spanned(
                                                &mod_path,
                                                "Value of mod_path must be a mod path",
                                            )
                                        })
                                    }) {
                                        Ok(Ok(tokens)) => Ok(Some(tokens)),
                                        Ok(Err(e)) => Err(e),
                                        Err(e) => Err(e),
                                    }
                                }
                            }
                            _ => Err(Error::new_spanned(
                                &attr,
                                "Argument of `Iroha` must be a meta.",
                            )),
                        };
                    }
                }
                Err(e) => mod_path_tokens = Err(e),
                _ => {
                    mod_path_tokens = Err(Error::new_spanned(
                        &attr,
                        "Attribute `Iroha` only support list.",
                    ))
                }
            }
        }
    }

    let mod_path_tokens = match mod_path_tokens {
        Ok(result) => result,
        Err(e) => return TokenStream::from(e.to_compile_error()),
    };

    let result = match &input.data {
        Data::Enum(_) => {
            EnumStructure::from_ast(&input, mod_path_tokens).map(|s| s.get_implement())
        }
        Data::Struct(_) => {
            StructStructure::from_ast(&input, mod_path_tokens).map(|s| s.get_implement())
        }
        _ => Err(Error::new_spanned(&input, "Unknown data type")),
    };

    //panic!(result.unwrap().unwrap().to_string());

    TokenStream::from(match result {
        Ok(Ok(tokens)) => tokens,
        Ok(Err(e)) => e.to_compile_error(),
        Err(e) => e.to_compile_error(),
    })
}