Skip to main content

artis_derive/
lib.rs

1use proc_macro2::{Literal, TokenStream, TokenTree};
2use quote::quote;
3use syn::{
4    parse_macro_input, Attribute, DataStruct, DeriveInput, GenericArgument, PathSegment, Type,
5};
6
7fn extrat_colume(v: &PathSegment) -> String {
8    if v.arguments.is_empty() {
9        return v.ident.to_string();
10    }
11    let raw = v.ident.to_string();
12    match raw.as_str() {
13        "Vec" => return "Vec".into(),
14        "HashMap" => return "Map".into(),
15        _ => {}
16    };
17    if let syn::PathArguments::AngleBracketed(v) = &v.arguments {
18        if let GenericArgument::Type(v) = v.args.first().unwrap() {
19            return extrat_type(v);
20        }
21    }
22    "".into()
23}
24
25fn extrat_type(t: &syn::Type) -> String {
26    if let Type::Path(v) = t {
27        return extrat_colume(v.path.segments.first().unwrap());
28    }
29    return "".into();
30}
31
32#[derive(Debug, Clone)]
33struct Artis {
34    pub table: String,
35    pub name: String,
36    pub typ: String,
37    pub size: Option<TokenTree>,
38    pub nonull: bool,
39    pub index: bool,
40    pub unique: bool,
41    pub primary: bool,
42    pub default: String,
43    pub comment: String,
44    pub increment: bool,
45}
46
47impl Default for Artis {
48    fn default() -> Self {
49        Self {
50            table: "".into(),
51            name: "".into(),
52            typ: "".into(),
53            size: Some(Literal::i32_unsuffixed(0).into()),
54            nonull: false,
55            index: false,
56            unique: false,
57            primary: false,
58            default: "".into(),
59            comment: "".into(),
60            increment: false,
61        }
62    }
63}
64
65fn extrat_literal(v: Option<TokenTree>, trim: bool) -> String {
66    if v.is_none() {
67        return "".into();
68    }
69    if trim {
70        v.unwrap().to_string().trim().trim_matches('"').to_string()
71    } else {
72        v.unwrap().to_string().trim().replace("\"", "'")
73    }
74}
75
76impl From<TokenStream> for Artis {
77    fn from(value: TokenStream) -> Self {
78        let mut itr = value.into_iter();
79        let mut artis = Artis::default();
80        while let Some(v) = itr.next() {
81            let raw = v.to_string();
82            match raw.as_str() {
83                "table" => {
84                    itr.next();
85                    artis.table = extrat_literal(itr.next(), true);
86                }
87                "type" => {
88                    itr.next();
89                    artis.typ = extrat_literal(itr.next(), true);
90                }
91                "size" => {
92                    itr.next();
93                    artis.size = itr.next(); //ktol extrat_literal(itr.next()).parse::<i32>().unwrap();
94                }
95                "default" => {
96                    itr.next();
97                    artis.default = extrat_literal(itr.next(), false);
98                }
99                "comment" => {
100                    itr.next();
101                    artis.comment = extrat_literal(itr.next(), false);
102                }
103                "INDEX" => {
104                    artis.index = true;
105                }
106                "UNIQUE" => {
107                    artis.unique = true;
108                }
109                "NOT_NULL" => {
110                    artis.nonull = true;
111                }
112                "PRIMARY" => {
113                    artis.primary = true;
114                    artis.nonull = true;
115                }
116                "AUTO_INCREMENT" => {
117                    artis.increment = true;
118                }
119                _ => {}
120            }
121        }
122        artis
123    }
124}
125
126fn extrat_attrs(list: Vec<Attribute>) -> Option<Artis> {
127    for v in list {
128        let meta = &v.meta.require_list().unwrap();
129        if !meta.path.is_ident("artis") {
130            continue;
131        }
132        return Some(meta.tokens.clone().into());
133    }
134    None
135}
136
137fn extend_feilds(v: &DataStruct) -> Vec<Artis> {
138    let mut fields: Vec<Artis> = vec![];
139    for field in &v.fields {
140        let mut artis = Artis::default();
141        if let Some(v) = extrat_attrs(field.attrs.clone()) {
142            artis = v;
143        }
144        artis.name = field.ident.as_ref().unwrap().to_string();
145        if artis.typ.is_empty() {
146            artis.typ = format!(":{}", extrat_type(&field.ty));
147        }
148        fields.push(artis);
149    }
150    fields
151}
152
153#[proc_macro_derive(Artis, attributes(artis))]
154pub fn device_artis(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
155    let input: DeriveInput = parse_macro_input!(input);
156    let name = input.ident;
157    let mut table = format!("{}s", name.to_string().to_lowercase());
158    if let Some(v) = extrat_attrs(input.attrs) {
159        if !v.table.is_empty() {
160            table = v.table
161        }
162    }
163
164    let mut inx_quote: Vec<TokenStream> = vec![];
165    let mut com_quote: Vec<TokenStream> = vec![];
166    let mut primary = String::new();
167    if let syn::Data::Struct(s) = input.data {
168        let fields = extend_feilds(&s);
169        for field in fields {
170            let name = &field.name;
171            let colume = field.typ;
172            let size = field.size;
173            let nullable = !field.nonull;
174            let default = field.default;
175            let comment = field.comment;
176            let increment = field.increment;
177            let quote = quote! {artis::migrator::ColumeMeta {
178                name:#name.into(),
179                colume: #colume.into(),
180                size: #size,
181                nullable: #nullable,
182                default: #default.into(),
183                comment: #comment.into(),
184                increment:#increment
185            }};
186            com_quote.push(quote.into());
187
188            if field.primary {
189                primary = field.name;
190                continue;
191            }
192            if field.unique {
193                inx_quote.push(quote! {
194                    artis::migrator::IndexMeta::Unique(#name.into())
195                });
196                continue;
197            }
198            if field.index {
199                inx_quote.push(quote! {
200                    artis::migrator::IndexMeta::Index(#name.into())
201                });
202            }
203        }
204    }
205    quote! {
206        impl artis::migrator::ArtisMigrator for #name {
207            fn migrator() -> artis::migrator::TableMeta {
208                artis::migrator::TableMeta {
209                    name: #table.into(),
210                    primary: #primary.into(),
211                    columes: vec![#(#com_quote,)*],
212                    indexs: vec![#(#inx_quote,)*]
213                }
214            }
215        }
216    }
217    .into()
218}