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
use proc_macro::{TokenStream};
use quote::{quote};
use syn::{Data, DataStruct, DeriveInput, Fields, Meta, NestedMeta, parse_macro_input};


#[proc_macro_derive(SqlxModel, attributes(sqlx_model))]
// model 自动填充方法
pub fn derive_model(item: TokenStream) -> TokenStream {
    let input = parse_macro_input!(item as DeriveInput);
    let struct_name = &input.ident;

    let mut table_name=None;
    let mut table_pk=vec![];
    for attr in input.attrs.iter().filter(|e|{
        e.path.is_ident("sqlx_model")
    })
    {
        let meta = attr
            .parse_meta()
            .map_err(|e| syn::Error::new_spanned(attr, e))
            .unwrap();
        match meta {
            Meta::List(list) => {
                for cattr in list.nested.iter(){
                    match cattr {
                        NestedMeta::Meta(Meta::NameValue(ref attr_ident)) =>{
                            let name=attr_ident.clone();
                            let name=name.path.get_ident().unwrap().to_string();
                            let name=name.as_str();
                            let ident=attr_ident.clone();
                            match name {
                                "table_name"=>{
                                    let val= match ident.lit {
                                        syn::Lit::Str(val)=>val,
                                        _=>unreachable!("table name must be string"),
                                    }.value();
                                    table_name=Some(val);
                                },
                                "table_pk"=>{
                                    let val= match ident.lit {
                                        syn::Lit::Str(val)=>val,
                                        _=>unreachable!("table pk field must be string"),
                                    }.value();
                                    table_pk.push(val);
                                },
                                _=>{}
                            }
                        }
                        _ => {},
                    }
                }

            }
            _ => {}
        }
    }
    let table_name =table_name.unwrap_or_else(||{
        let mut name=struct_name.to_string();
        if name.clone().drain(0..5).collect::<String>()=="Model" {
            name=name.drain(5..).collect::<String>();
        }
        if name.clone().drain(name.len()-5..).collect::<String>()=="Model" {
            name=name.drain(0..name.len()-5).collect::<String>();
        }
        name.chars().enumerate().map(|(i,e)|{
            if i!=0 && e as u8>=65&&e as u8<=90 {
                format!("_{}",e.to_ascii_lowercase())
            }else{
                e.to_ascii_lowercase().to_string()
            }
        }).collect::<Vec<String>>().join("")
    });
    let expanded = match input.data {
        Data::Struct(DataStruct{ref fields,..}) => {
            if let Fields::Named(ref fields_name) = fields {
                let change_fields: Vec<_> = fields_name.named.iter().map(|field| {
                    let field_name = field.ident.as_ref().unwrap();
                    let field_type = field.ty.clone();
                    quote! {
                        #field_name:#field_type
                    }
                }).collect();
                let bind_fields: Vec<_> = fields_name.named.iter().map(|field| {
                    let field_name = field.ident.as_ref().unwrap();
                    quote! {
                        #field_name
                    }
                }).collect();
                let change_struct=quote::format_ident!("{}Change",struct_name);
                let mut pk_fields=vec![];
                for field in fields_name.named.iter() {
                    let field_name = field.ident.as_ref().unwrap();
                    if table_pk.contains(&field_name.to_string()) {
                        pk_fields.push(field_name.to_owned());
                    }
                }
                if pk_fields.len()==0 {
                    if let Some(field)=fields_name.named.iter().next()  {
                        let field_name = field.ident.as_ref().unwrap();
                        pk_fields.push(field_name.to_owned());
                    }
                }
                let implemented_show = quote! {
                    sqlx_model::model_table_value_bind_define!(#struct_name,#table_name,{#(#bind_fields),*},{#(#pk_fields),*});
                    sqlx_model::model_table_option_define!(#struct_name,#change_struct,{#(#change_fields),*});
                };
                implemented_show

            } else {
                panic!("sorry, may it's a complicated struct.");
            }
        }
        _ => panic!("sorry, Show is not implemented for union or enum type.")
    };
    expanded.into()
}