1extern crate proc_macro;
2use proc_macro::TokenStream;
3use proc_macro2::Span;
4use quote::{format_ident, quote};
5use syn::{parse_macro_input, Data, DataStruct, DeriveInput, Fields, Ident};
6
7#[proc_macro_derive(Table)]
8pub fn table(input: TokenStream) -> TokenStream {
9 let input = parse_macro_input!(input as DeriveInput);
10
11 if let Data::Struct(DataStruct {
12 fields: Fields::Named(fields),
13 ..
14 }) = input.data
15 {
16 let ident = &input.ident;
17 let fields_ident = {
18 let mut s = ident.to_string();
19 s.push_str("Fields");
20 Ident::new(&s, Span::call_site())
21 };
22
23 let struct_fields = fields.named.iter().map(|field| {
24 let name = &field.ident;
25 let ty = &field.ty;
26 quote! {
27 #name: typed_sql::types::Field::<#ident, #ty>,
28 }
29 });
30
31 let default_fields = fields.named.iter().map(|field| {
32 let name = &field.ident;
33 quote! {
34 #name: typed_sql::types::Field::new(stringify!(#name)),
35 }
36 });
37
38 let table_name = {
39 let mut s = ident.to_string().to_lowercase();
40 s.push('s');
41 Ident::new(&s, Span::call_site())
42 };
43
44 let expanded = quote! {
45 struct #fields_ident {
46 #(#struct_fields)*
47 }
48
49 impl Default for #fields_ident {
50 fn default() -> Self {
51 Self {
52 #(#default_fields)*
53 }
54 }
55 }
56
57 impl typed_sql::Table for #ident {
58 const NAME: &'static str = stringify!(#table_name);
59
60 type Fields = #fields_ident;
61 }
62 };
63
64 TokenStream::from(expanded)
65 } else {
66 todo!()
67 }
68}
69
70#[proc_macro_derive(Join)]
71pub fn join(input: TokenStream) -> TokenStream {
72 let input = parse_macro_input!(input as DeriveInput);
73
74 if let Data::Struct(DataStruct {
75 fields: Fields::Named(fields),
76 ..
77 }) = input.data
78 {
79 let ident = input.ident;
80 let fields_ident = format_ident!("{}Fields", ident);
81
82 let struct_fields = fields.named.iter().map(|field| {
83 let name = &field.ident;
84 let ty = &field.ty;
85 quote! {
86 #name: <#ty as typed_sql::Table>::Fields
87 }
88 });
89
90 let mut fields = fields.named.iter();
91 let table = &fields.next().unwrap().ty;
92
93 let join_ident = format_ident!("{}Join", ident);
94 let join_fields = fields.clone().map(|field| {
95 let name = &field.ident;
96 let g = field.ident.as_ref().unwrap().to_string().to_uppercase();
97 let g = format_ident!("{}", g);
98 let ty = &field.ty;
99 quote! {
100 #name: typed_sql::query::select::join::Joined<#g, typed_sql::query::select::join::Inner, #ty>
101 }
102 });
103
104 let generics = fields.map(|field| {
105 Ident::new(
106 &field.ident.as_ref().unwrap().to_string().to_uppercase(),
107 Span::call_site(),
108 )
109 });
110
111 let join_generics = generics.clone().map(|generic| {
112 quote! {
113 #generic
114 }
115 });
116 let join_generics = quote! {
117 #(#join_generics),*
118 };
119
120 let impl_generics = generics.clone().map(|generic| {
121 quote! {
122 #generic: typed_sql::query::Predicate
123 }
124 });
125 let impl_generics = quote! {
126 #(#impl_generics),*
127 };
128
129 let expanded = quote! {
130 #[derive(Default)]
131 struct #fields_ident {
132 #(#struct_fields),*
133 }
134
135 struct #join_ident<#join_generics> {
136 #(#join_fields),*
137 }
138
139 impl<#impl_generics> typed_sql::Join<(#join_generics)> for #ident {
140 type Table = #table;
141 type Fields = #fields_ident;
142 type Join = #join_ident<#join_generics>;
143 }
144
145 impl<#impl_generics> typed_sql::query::select::join::JoinSelect for #join_ident<#join_generics> {
146 type Table = #table;
147 type Fields = #fields_ident;
148
149 fn write_join_select(&self, sql: &mut String) {
150 self.post.write_join(sql);
151 }
152 }
153 };
154
155 TokenStream::from(expanded)
156 } else {
157 todo!()
158 }
159}
160
161#[proc_macro_derive(Insertable)]
162pub fn insertable(input: TokenStream) -> TokenStream {
163 let input = parse_macro_input!(input as DeriveInput);
164
165 if let Data::Struct(DataStruct {
166 fields: Fields::Named(fields),
167 ..
168 }) = input.data
169 {
170 let ident = &input.ident;
171
172 let write_columns = fields.named.iter().map(|field| {
173 let name = &field.ident;
174 quote! { sql.push_str(stringify!(#name)); }
175 });
176
177 let write_values = fields.named.iter().map(|field| {
178 let name = &field.ident;
179 quote! { self.#name.write_primative(sql); }
180 });
181
182 let expanded = quote! {
183 impl typed_sql::Insertable for #ident {
184 fn write_columns(sql: &mut String) {
185 #(#write_columns)(sql.push(',');)*
186 }
187
188 fn write_values(&self, sql: &mut String) {
189 use typed_sql::types::Primitive;
190 #(#write_values)(sql.push(',');)*
191 }
192 }
193 };
194 TokenStream::from(expanded)
195 } else {
196 todo!()
197 }
198}
199
200#[proc_macro_derive(Binding)]
201pub fn binding(input: TokenStream) -> TokenStream {
202 let input = parse_macro_input!(input as DeriveInput);
203
204 if let Data::Struct(DataStruct {
205 fields: Fields::Named(fields),
206 ..
207 }) = input.data
208 {
209 let ident = &input.ident;
210 let bindings = format_ident!("{}Bindings", ident);
211
212 let bind_fields = fields.named.iter().map(|field| {
213 let name = &field.ident;
214 quote! { #name: typed_sql::types::Bind }
215 });
216
217 let binds = fields.named.iter().map(|field| {
218 let name = &field.ident;
219 quote! { #name: binder.bind() }
220 });
221
222 let values = fields.named.iter().map(|field| {
223 let name = &field.ident;
224 quote! { self.#name.write_primative(sql); }
225 });
226
227 let expanded = quote! {
228 struct #bindings {
229 #(#bind_fields),*
230 }
231
232 impl typed_sql::Binding for #ident {
233 type Bindings = #bindings;
234
235 fn bindings(binder: &mut typed_sql::types::bind::Binder) -> Self::Bindings {
236 #bindings {
237 #(#binds),*
238 }
239 }
240
241 fn write_types(_sql: &mut String) {}
242
243 fn write_values(&self, sql: &mut String) {
244 use typed_sql::types::Primitive;
245 #(#values)(sql.push(','))*;
246 }
247 }
248 };
249 TokenStream::from(expanded)
250 } else {
251 todo!()
252 }
253}
254
255#[proc_macro_derive(Queryable)]
256pub fn queryable(input: TokenStream) -> TokenStream {
257 let input = parse_macro_input!(input as DeriveInput);
258
259 if let Data::Struct(DataStruct {
260 fields: Fields::Named(fields),
261 ..
262 }) = input.data
263 {
264 let ident = &input.ident;
265 let columns = fields.named.iter().map(|field| {
266 let name = &field.ident;
267 quote! {
268 sql.push_str(stringify!(#name));
269 }
270 });
271
272 let expanded = quote! {
273 impl typed_sql::Queryable for #ident {
274 fn write_queryable(sql: &mut String) {
275 #(#columns)(sql.push(','))*
276 }
277 }
278 };
279 TokenStream::from(expanded)
280 } else {
281 todo!()
282 }
283}