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
96
97
98
99
100
101
102
103
104
#![allow(unused_variables)]

use crate::def::*;
use codegen::{Formatter, Function, Scope};
use proc_macro2::TokenStream;

pub fn generate_type_from_field_ty(ty: Type) -> String {
    match ty {
        Type::Atom(atom) => format!("Type::Atom(\"{}\".to_string())", atom),
        Type::Vec(inner) => format!("Type::Vec({})", generate_type_from_field_ty(*inner)),
    }
}

pub fn generate_string_from_struct_def(struct_def: &StructDef) -> String {
    format!(
        "StructDef {{
        name: \"{}\".to_string(),
        fields: vec![{}],
    }}",
        struct_def.name,
        struct_def.fields.iter().fold("\n".to_string(), |v, field| {
            let code = format!(
                "StructField {{
            name: \"{}\".to_string(),
            ty: {}
        }}",
                field.name,
                generate_type_from_field_ty(field.ty.clone())
            );
            if v.len() <= 1 {
                code
            } else {
                format!("{v}, {code}")
            }
        })
    )
}

pub fn generate_scope_from_struct_defs(struct_defs: Vec<StructDef>) -> Scope {
    let mut scope = Scope::new();

    scope.import("rusto::def", "*");

    for struct_def in struct_defs.iter() {
        let mut func = Function::new("get_structs");
        func.vis("pub");
        func.ret(codegen::Type::new("StructDef"));
        func.line(generate_string_from_struct_def(struct_def));
        scope.push_fn(func);
    }

    scope
}

pub fn generate_scope(token_stream: TokenStream) -> Scope {
    let struct_defs = get_struct_defs(token_stream);
    generate_scope_from_struct_defs(struct_defs)
}

pub fn generate(token_stream: TokenStream) -> String {
    let scope = generate_scope(token_stream);
    scope.to_string()
}

pub fn generate_format(token_stream: TokenStream) -> String {
    let mut code = generate(token_stream);
    let mut formatter = Formatter::new(&mut code);
    formatter.indent(move |&mut _| 1);
    code
}

#[cfg(test)]
mod tests {
    use super::*;
    use quote::quote;

    fn get_code() -> TokenStream {
        return quote! {
            struct Person {
                name: String,
                age: u8
            }
        };
    }

    #[test]
    fn generate_test() {
        let code = generate_format(get_code());
        assert_eq!(code, "use rusto::def::*;

pub fn get_structs() -> StructDef {
    StructDef {
            name: \"Person\".to_string(),
            fields: vec![StructField {
                name: \"name\".to_string(),
                ty: Type::Atom(\"String\".to_string())
            }, StructField {
                name: \"age\".to_string(),
                ty: Type::Atom(\"u8\".to_string())
            }],
        }
}".to_string());
    }
}