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(); }
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}