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
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
use inflector::Inflector;
use proc_macro2::TokenStream;
use quote::*;

use crate::ir::*;

impl ToTokens for TypeDecl {
    fn to_tokens(&self, tokens: &mut TokenStream) {
        match self {
            TypeDecl::Simple(simple) => simple.to_tokens(tokens),
            TypeDecl::Rename(rename) => rename.to_tokens(tokens),
            TypeDecl::Enumeration(e) => e.to_tokens(tokens),
            TypeDecl::Select(select) => select.to_tokens(tokens),
        }
    }
}

impl ToTokens for Simple {
    fn to_tokens(&self, tokens: &mut TokenStream) {
        let field_name = format_ident!("{}", &self.id.to_snake_case());
        let id = format_ident!("{}", &self.id.to_pascal_case());
        let ty = &self.ty;
        let (derive, _) = simple_meta(&field_name);
        tokens.append_all(quote! {
            #derive
            pub struct #id(pub #ty);
        });
    }
}

impl ToTokens for Rename {
    fn to_tokens(&self, tokens: &mut TokenStream) {
        let field_name = format_ident!("{}", &self.id.to_snake_case());
        let id = format_ident!("{}", &self.id.to_pascal_case());
        let ty = &self.ty;
        let (derive, use_place_holder) = match ty {
            TypeRef::SimpleType(_) => simple_meta(&field_name),
            TypeRef::Named { is_enumerate, .. } => {
                // Enumeration does not have Holder.
                if *is_enumerate {
                    simple_meta(&field_name)
                } else {
                    rename_meta(&field_name)
                }
            }
            _ => rename_meta(&field_name),
        };

        tokens.append_all(quote! {
            #derive
            pub struct #id(#use_place_holder pub #ty);
        });
    }
}

fn simple_meta(field_name: &syn::Ident) -> (TokenStream, TokenStream) {
    (
        quote! {
            #[derive(Clone, Debug, PartialEq, AsRef, Deref, DerefMut, Into, From, ::ruststep_derive::Holder)]
            #[holder(table = Tables)]
            #[holder(field = #field_name)]
            #[holder(generate_deserialize)]
        },
        quote! {},
    )
}

fn rename_meta(field_name: &syn::Ident) -> (TokenStream, TokenStream) {
    (
        quote! {
            #[derive(Clone, Debug, PartialEq, AsRef, Deref, DerefMut, Into, From, ::ruststep_derive::Holder)]
            #[holder(table = Tables)]
            #[holder(field = #field_name)]
            #[holder(generate_deserialize)]
        },
        quote! {#[holder(use_place_holder)]},
    )
}

impl ToTokens for Enumeration {
    fn to_tokens(&self, tokens: &mut TokenStream) {
        let id = format_ident!("{}", &self.id.to_pascal_case());
        let items: Vec<_> = self
            .items
            .iter()
            .map(|i| format_ident!("{}", i.to_pascal_case()))
            .collect();
        tokens.append_all(quote! {
            #[derive(Debug, Clone, PartialEq, ::serde::Deserialize)]
            pub enum #id {
                #( #items ),*
            }
        });
    }
}

impl ToTokens for Select {
    fn to_tokens(&self, tokens: &mut TokenStream) {
        let id = format_ident!("{}", &self.id.to_pascal_case());
        let mut entries = Vec::new();
        let mut entry_types = Vec::new();
        let mut use_place_holder = Vec::new();
        for ty in &self.types {
            match ty {
                TypeRef::Entity {
                    name, is_supertype, ..
                } => {
                    entries.push(format_ident!("{}", name.to_pascal_case()));
                    if *is_supertype {
                        entry_types.push(quote! { #ty });
                    } else {
                        entry_types.push(quote! { Box<#ty> });
                    }
                    use_place_holder.push(quote! { #[holder(use_place_holder)] });
                }
                TypeRef::Named {
                    name, is_enumerate, ..
                } => {
                    entries.push(format_ident!("{}", name.to_pascal_case()));
                    if *is_enumerate {
                        entry_types.push(quote! { #ty });
                        use_place_holder.push(quote! {});
                    } else {
                        entry_types.push(quote! { Box<#ty> });
                        use_place_holder.push(quote! { #[holder(use_place_holder)] });
                    }
                }
                _ => unimplemented!(),
            }
        }
        tokens.append_all(quote! {
            #[derive(Debug, Clone, PartialEq, Holder)]
            #[holder(table = Tables)]
            #[holder(generate_deserialize)]
            pub enum #id {
                #(
                #use_place_holder
                #entries(#entry_types)
                ),*
            }
        });
    }
}