anansi_macros/
lib.rs

1extern crate proc_macro2;
2use std::collections::HashMap;
3use proc_macro2::TokenStream;
4use syn::{parse_macro_input, DeriveInput, Data, Fields, Ident, ItemFn, Token, Attribute, Expr, FieldValue};
5use syn::{Type, GenericParam, ItemImpl};
6use syn::Type::Path;
7use syn::token::Comma;
8use syn::Data::Struct;
9use syn::spanned::Spanned;
10use syn::punctuated::Punctuated;
11use syn::parse::{Parse, Parser, ParseStream, Result};
12use quote::{quote, quote_spanned, format_ident};
13
14use syn::Pat;
15use syn::FnArg::Typed;
16
17#[proc_macro]
18pub fn release(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
19    let input = parse_macro_input!(input as Ident);
20    let raw_input = format_ident!("_{}", input);
21    quote !{
22        drop(#input);
23        drop(#raw_input);
24    }.into()
25}
26
27#[proc_macro]
28pub fn min_main(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
29    let input = parse_macro_input!(input as SchemaArgs);
30    let (server, init) = if input.vars.len() == 2 {
31        let n = &input.vars[0];
32        let b = &input.vars[1];
33        (quote! {#n}, quote! {#b})
34    } else {
35        (quote! {server}, quote! {})
36    };
37    let q = quote! {
38        pub mod prelude {
39            pub use async_trait::async_trait;
40            pub use crate::project::Request;
41            pub use anansi::{form, import, viewer, base_view, view, redirect, transact, form_error};
42            pub use anansi::web::{Result, Response, BaseUser};
43            pub use anansi::forms::Form;
44            pub use anansi::cache::BaseCache;
45            pub use anansi::records::Record;
46            pub use anansi::site::Site;
47        }
48
49        async fn serve(last: bool) {
50            use server_prelude::*;
51            use crate::project::*;
52
53            let app_data = AppData::new().await;
54            let internal_error = || Response::internal_error(include_bytes!("http_errors/500.html").to_vec());
55            if let Some(#server) = anansi::server::Server::<HttpRequest, AppCache, AppData, anansi::web::SecurityHeaders<anansi::web::ViewService>>::new(app_data, APP_STATICS, None, urls::routes, ErrorView::not_found, internal_error, app_services::<HttpRequest>, app_migrations, last).await {
56                #init
57                #server.run().await;
58            }
59        }
60
61        fn main() {
62            anansi::server::init_logger();
63            let rt = tokio::runtime::Builder::new_current_thread()
64                .enable_all()
65                .build()
66                .unwrap();
67            let mut handles = vec![];
68            for _ in 1..std::thread::available_parallelism().map(|n| n.get()).unwrap_or(16) {
69                handles.push(std::thread::spawn(move || {
70                    let rt = tokio::runtime::Builder::new_current_thread()
71                        .enable_all()
72                        .build()
73                        .unwrap();
74                    rt.block_on(serve(false));
75                }));
76            }
77            rt.block_on(serve(true));
78            for handle in handles{
79                handle.join().unwrap();
80            }
81            println!("Shutting down");
82        }
83
84        mod server_prelude {
85            pub use std::sync::{Arc, Mutex};
86            pub use crate::http_errors::views::ErrorView;
87            pub use crate::project::{app_services, HttpRequest};
88            pub use anansi::web::Response;
89        }
90
91        #[cfg(test)]
92        pub async fn test_server(sender: tokio::sync::oneshot::Sender<()>) {
93            use server_prelude::*;
94
95            let pool = anansi::server::get_db::<Pool>(app_migrations).await?;
96            let app_data = AppData {pool};
97            let internal_error = || Response::internal_error(include_bytes!("http_errors/500.html").to_vec());
98            anansi::server::Server::new(app_data, APP_STATICS, APP_ADMINS, Some(sender))
99                .run(app_url, urls::ROUTES, ErrorView::not_found, internal_error, app_services::<HttpRequest>, app_migrations)
100                .await;
101        }
102    };
103    q.into()
104}
105
106#[proc_macro_derive(Record, attributes(field))]
107pub fn record_macro_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
108    let input = parse_macro_input!(input as DeriveInput);
109    let name = input.ident;
110    let lowercase = name.to_string().to_lowercase();
111    let lowname = format_ident!("{}", lowercase);
112    let mut fv = Vec::new();
113    let mut fv2 = Vec::new();
114    let mut members = Vec::new();
115    let mut pkd = PkData::new();
116    let init = record_init(&name, &lowercase, &input.data, &mut pkd, &mut members, &mut fv, &mut fv2);
117    let pk_name = &pkd.name;
118    let pk_id = format_ident!("{}", pkd.name);
119    let values = &pkd.values;
120    let params = &pkd.params;
121    let mut sets = Vec::new();
122    let mut saves = Vec::new();
123    for member in &members {
124        let lowmem = member.to_lowercase();
125        let m = format_ident!("{}", member);
126        sets.push(quote!{.set(#lowmem, &self.#m)});
127        saves.push(quote!{.value(&self.#m)});
128    }
129    let (pt, _pkty, pdt) = match pkd.ty.as_str() {
130        "BigInt" => (quote! {anansi::records::BigInt}, quote! {i64}, quote! {anansi::records::Record}),
131        "Int" => (quote! {anansi::records::Int}, quote! {i32}, quote! {anansi::records::Record}),
132        _ => unimplemented!(),
133    };
134    for (fname, fks) in &pkd.fkv {
135        let fkn: Vec<&str> = fks.split("::").collect();
136        let fk = fkn.last().unwrap().trim().to_string();
137        let full: Type = syn::parse_str(fks).unwrap();
138        let by_record = format_ident!("by_{}", fk.to_string().to_lowercase());
139        let recordset = format_ident!("{}{}Set", fk, name);
140        let mut mv = Vec::new();
141        let mut mtv = Vec::new();
142        let pkn = format_ident!("{}", pk_name);
143        for (null, mt0, mt1) in &pkd.member_type {
144            if mt0 != fname && *mt0 != pkn {
145                mv.push(quote! {#mt0});
146                mtv.push(quote! {#mt0: #mt1});
147            } else if mt0 == fname {
148                if !null {
149                    mv.push(quote! {ForeignKey::new(self.record)});
150                } else {
151                    mv.push(quote! {Some(ForeignKey::new(self.record))});
152                }
153            }
154        }
155        let q3 = quote! {
156            impl #name {
157                pub fn #by_record(record: &#full) -> #recordset {
158                    #recordset {record}
159                }
160            }
161            pub struct #recordset<'a> {
162                record: &'a #full,
163            }
164            impl<'a> #recordset<'a> {
165                pub fn new(&self, #(#mtv),*) -> #name {
166                    #name::_new(#(#mv),*)
167                }
168                pub fn order_by(&self, o: anansi::db::OrderByArg<#name>) -> anansi::db::OrderBy<#name> {
169                    use anansi::records::Record;
170                    #name::whose(#fname().eq(<#full as #pdt>::pk(self.record))).order_by(o)
171                }
172            }
173        };
174        fv.push(q3);
175    }
176
177    let name_string = name.to_string();
178    let table = quote! {&Self::table()};
179    let table_name = quote! {Self::table()};
180    let record_fields = format_ident!("{}Fields", name);
181    
182    let primary = quote! {<#name as #pdt>::pk(&self)};
183    let expanded = quote! {
184        #[async_trait::async_trait]
185        impl anansi::records::Record for #name {
186            type Pk = #pt;
187            const NAME: &'static str = #name_string;
188            const PK_NAME: &'static str = #pk_name;
189            fn pk(&self) -> #pt {
190                self.#pk_id.clone()
191            }
192            fn pk_mut(&mut self) -> &mut #pt {
193                &mut self.#pk_id
194            }
195            fn find(d: #pt) -> anansi::db::Whose<Self> {
196                Self::whose(#lowname::pk().eq(d))
197            }
198            fn find_in(keys: &Vec<#pt>) -> anansi::db::Limit<Self> {
199                assert!(!keys.is_empty());
200                Self::whose(#lowname::pk().is_in(keys)).order_by(#lowname::pk().field(keys)).limit(keys.len() as u32)
201            }
202            fn count() -> anansi::db::Count<Self> {
203                anansi::db::Count::from(anansi::db::Builder::count(#table))
204            }
205            fn whose(w: anansi::db::WhoseArg<Self>) -> anansi::db::Whose<Self> {
206                anansi::db::Whose::from(anansi::db::Builder::select(&[#(#members),*], #table).whose().append(w.builder()))
207            }
208            fn delete_whose(w: anansi::db::WhoseArg<Self>) -> anansi::db::DeleteWhose<Self> {
209                anansi::db::DeleteWhose::from(anansi::db::Builder::delete(#table).whose().append(w.builder()))
210            }
211            fn limit(n: u32) -> anansi::db::Limit<Self> {
212                anansi::db::Limit::from(anansi::db::Builder::select(&[#(#members),*], #table).limit(n))
213            }
214            fn get_all() -> anansi::db::Limit<Self> {
215                anansi::db::Limit::from(anansi::db::Builder::select(&[#(#members),*], #table))
216            }
217            fn get<R: anansi::db::DbRow>(row: R) -> anansi::web::Result<Self> {
218                Ok(Self {#init})
219            }
220            fn from<V: anansi::db::DbRowVec>(rows: V) -> anansi::web::Result<anansi::records::Objects<Self>> {
221                let mut mv = anansi::records::Objects::new();
222                for row in rows {
223                    mv.push(Self::get(row)?);
224                }
225                Ok(mv)
226            }
227            fn order_by(w: anansi::db::OrderByArg<Self>) -> anansi::db::OrderBy<Self> {
228                anansi::db::OrderBy::from(anansi::db::Builder::select(&[#(#members),*], #table).order_by().append(w.builder()))
229            }
230            fn table_name() -> String {
231                #table_name
232            }
233            async fn update<B: anansi::web::BaseRequest>(&mut self, req: &B) -> anansi::web::Result<()> {
234                self.raw_update(req.raw().pool()).await
235            }
236            async fn raw_update<D: anansi::db::DbPool>(&mut self, pool: &D) -> anansi::web::Result<()> {
237                let u: anansi::db::Update<Self> = anansi::db::Update::new(#table)
238                    #(#sets)*.pk(Self::PK_NAME, #primary);
239                u.raw_update(pool).await
240            }
241            async fn delete<B: anansi::web::BaseRequest>(&self, req: &B) -> anansi::web::Result<()> {
242                use anansi::records::Relate;
243                use anansi::db::DbPool;
244                req.raw().pool().raw_execute("BEGIN;").await?;
245                match self.on_delete(req).await {
246                    Ok(_) => match anansi::db::delete_from(#table, Self::PK_NAME, #primary, req).await {
247                        Ok(_) => req.raw().pool().raw_execute("COMMIT;").await,
248                        Err(_) => req.raw().pool().raw_execute("ROLLBACK;").await,
249                    }
250                    Err(_) => req.raw().pool().raw_execute("ROLLBACK;").await,
251                }
252            }
253            async fn save<R: anansi::web::BaseRequest>(&self, req: &mut R) -> anansi::web::Result<()> {
254                use anansi::records::Relate;
255                use anansi::db::DbPool;
256                req.raw().pool().raw_execute("BEGIN;").await?;
257                match self.on_save(req).await {
258                    Ok(_) => match self.raw_save(req.raw().pool()).await {
259                        Ok(_) => req.raw().pool().raw_execute("COMMIT;").await,
260                        Err(_) => req.raw().pool().raw_execute("ROLLBACK;").await,
261                    }
262                    Err(_) => req.raw().pool().raw_execute("ROLLBACK;").await,
263                }
264            }
265            async fn raw_save<D: anansi::db::DbPool>(&self, pool: &D) -> anansi::web::Result<()> {
266                let i: anansi::db::Insert<Self> = anansi::db::Insert::new(#table, &[#(#members),*])
267                    #(#saves)*;
268                i.raw_save(pool).await?;
269                Ok(())
270            }
271        }
272        impl #name {
273            pub fn _new(#(#params)*) -> Self {
274                Self {#(#values),*}
275            }
276        }
277        pub mod #lowname {
278            use super::*;
279            use anansi::db::Column;
280            #(#fv)*
281        }
282        pub struct #record_fields<F: anansi::records::Record> {
283            b: anansi::db::Builder<F>,
284        }
285        impl<F: anansi::records::Record> #record_fields<F> {
286            pub fn new(b: anansi::db::Builder<F>) -> Self {
287                Self {b}
288            }
289            #(#fv2)*
290        }
291    };
292    proc_macro::TokenStream::from(expanded)
293}
294
295struct PkData {
296    name: String,
297    ty: String,
298    params: Vec<TokenStream>,
299    values: Vec<TokenStream>,
300    fkv: Vec<(Ident, String)>,
301    member_type: Vec<(bool, Ident, TokenStream)>,
302}
303
304impl PkData {
305    fn new() -> Self {
306        Self {
307            name: String::new(),
308            ty: String::new(),
309            params: Vec::new(),
310            values: Vec::new(),
311            fkv: Vec::new(),
312            member_type: Vec::new(),
313        }
314    }
315}
316
317fn get_names(data: &Data) -> Vec<Ident> {
318    match *data {
319        Data::Struct(ref data) => {
320            match data.fields {
321                Fields::Named(ref fields) => {
322                    let mut v = vec![];
323                    for f in fields.named.iter().skip(4) {
324                        v.push(f.ident.as_ref().unwrap().clone());
325                    };
326                    return v;
327                },
328                _ => unimplemented!(),
329            }
330        },
331        _ => unimplemented!(),
332    }
333}
334
335#[proc_macro_derive(Properties)]
336pub fn properties_macro_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
337    builder(true, input)
338}
339
340#[proc_macro_derive(Builder)]
341pub fn builder_macro_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
342    builder(false, input)
343}
344
345fn builder(properties: bool, input: proc_macro::TokenStream) -> proc_macro::TokenStream {
346    let input = parse_macro_input!(input as DeriveInput);
347    let name = input.ident;
348    let mut fv = vec![];
349    let mut gtv = vec![];
350    let mut tv = vec![];
351    let mut nov = vec![];
352    let mut no_ids = vec![];
353    let mut no_init = vec![];
354    let mut data = vec![];
355    let mut ids = vec![];
356    let mut n = 0;
357    let mut field_structs = vec![];
358    let mut options = vec![];
359    let mut opt_ids = vec![];
360    let mut id_field = quote!{};
361    match &input.data {
362        syn::Data::Struct(data_struct) => {
363            for named in &data_struct.fields {
364                let ty = &named.ty;
365                let ty_quote = quote!{#ty};
366                let id = named.ident.as_ref().unwrap();
367                if ty_quote.to_string().starts_with("Option") {
368                    options.push((id, ty));
369                    no_init.push(quote!{#id: None});
370                    fv.push(quote!{#id: #ty});
371                    opt_ids.push(id.clone());
372                } else {
373                    if id == "id" {
374                        let ts = ty_quote.to_string();
375                        if ts.ends_with("BigInt") {
376                            id_field = quote! {id: anansi::records::generate_id(),};
377                        } else if ts.ends_with("Int") {
378                            id_field = quote! {id: anansi::records::random_int(),};
379                        } else {
380                            unimplemented!();
381                        }
382                    } else {
383                        let gen_ty = format_ident!("T{}", n.to_string());
384                        let f = format_ident!("{}{}", name, quote!{#id}.to_string().replace("_", "").to_uppercase());
385                        let no = format_ident!("No{}", f);
386                        n += 1;
387                        field_structs.push(f.clone());
388                        no_ids.push(no.clone());
389                        gtv.push(quote!{#gen_ty});
390                        tv.push(ty_quote);
391                        fv.push(quote!{#id: #gen_ty});
392                        no_init.push(quote!{#id: #no});
393                        ids.push(id.clone());
394                        nov.push(quote!{pub struct #f(#ty); pub struct #no;});
395                        data.push((id, gen_ty, no, ty, f));
396                    }
397                }
398            }
399        }
400        _ => unimplemented!(),
401    }
402    let builder = format_ident!("{}Builder", name);
403    let mut n = 0;
404    let l = data.len();
405    let mut methods = vec![];
406    for (id, _gty, no, ty, f) in data {
407        let mut t1 = vec![];
408        let mut t2 = vec![];
409        let mut t3 = vec![];
410        let mut sv = vec![];
411        for i in 0..l {
412            if i != n {
413                let gty = format_ident!("T{}", i);
414                t1.push(gty.clone());
415                t2.push(gty.clone());
416                t3.push(gty);
417                sv.push(&ids[i]);
418            } else {
419                t2.push(no.clone());
420                t3.push(f.clone());
421            }
422        }
423        n += 1;
424        let q = if properties {
425            quote! {
426                impl<#(#t1),*> #builder<#(#t2),*> {
427                    pub fn #id(self, #id: impl Into<#ty>) -> #builder<#(#t3),*> {
428                        let Self {#(#sv,)* #(#opt_ids,)* ..} = self;
429                        #builder {
430                            #id: #f(#id.into()),
431                            #(#sv,)*
432                            #(#opt_ids,)*
433                        }
434                    }
435                }
436            }
437        } else {
438            quote! {
439                impl<#(#t1),*> #builder<#(#t2),*> {
440                    pub fn #id(self, #id: #ty) -> #builder<#(#t3),*> {
441                        let Self {#(#sv,)* #(#opt_ids,)* ..} = self;
442                        #builder {
443                            #id: #f(#id),
444                            #(#sv,)*
445                            #(#opt_ids,)*
446                        }
447                    }
448                }
449            }
450        };
451        methods.push(q);
452    }
453    
454    let opts = if options.is_empty() {
455        vec![]
456    } else {
457        let mut gen_tys = vec![];
458        let mut option_methods = vec![];
459        for i in 0..methods.len() {
460            gen_tys.push(format_ident!("T{}", i));
461        }
462        for (id, ty) in options {
463            option_methods.push(quote! {
464                impl<#(#gen_tys),*> #builder<#(#gen_tys),*> {
465                    pub fn #id(mut self, #id: #ty) -> Self {
466                        self.#id = #id;
467                        self
468                    }
469                }
470            });
471        }
472        option_methods
473    };
474
475    let last = if properties {
476        quote! {
477            #(#opts)*
478            impl #builder<#(#field_structs),*> {
479                pub fn build(self) -> #name {
480                    let Self {#(#ids),*} = self;
481                    #name {
482                        #(#ids: #ids.0),*
483                    }
484                }
485            }
486            impl #name {
487                pub fn resume(store: &mut anansi_aux::AppState, n: usize) -> Self {
488                    if let anansi_aux::Obj::Js(v) = &store.objs()[n] {
489                        let value: Self = serde_json::from_value(v.clone()).unwrap();
490                        value
491                    } else {
492                        panic!("expected Rust type");
493                    }
494                }
495            }
496        }
497    } else {
498        let q = quote! {
499            #(#opts)*
500            impl #builder<#(#field_structs),*> {
501                pub async fn saved<B: anansi::web::BaseRequest>(self, req: &mut B) -> anansi::web::Result<#name> {
502                    use anansi::records::Record;
503                    let Self {#(#ids),*, #(#opt_ids),*} = self;
504                    let model = #name {
505                        #id_field
506                        #(#ids: #ids.0),*,
507                        #(#opt_ids),*
508                    };
509                    model.save(req).await?;
510                    Ok(model)
511                }
512                pub async fn raw_saved<D: anansi::db::DbPool>(self, pool: &D) -> anansi::web::Result<#name> {
513                    use anansi::records::Record;
514                    let Self {#(#ids),*, #(#opt_ids),*} = self;
515                    let model = #name {
516                        #id_field
517                        #(#ids: #ids.0),*,
518                        #(#opt_ids),*
519                    };
520                    model.raw_save(pool).await?;
521                    Ok(model)
522                }
523            }
524        };
525        q
526    };
527
528    let expanded = quote! {
529        #(#nov)*
530
531        #(#methods)*
532
533        pub struct #builder<#(#gtv),*> {
534            #(#fv),*
535        }
536
537        impl #name {
538            pub fn new() -> #builder<#(#no_ids),*> {
539                #builder {#(#no_init),*}
540            }
541        }
542        
543        #last
544    };
545
546    expanded.into()
547}
548
549#[proc_macro_derive(GetData)]
550pub fn get_data_macro_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
551    let input = parse_macro_input!(input as DeriveInput);
552    let name = input.ident;
553    let field_names = get_names(&input.data);
554    let mut v = vec![];
555    for field_name in &field_names {
556        let s = field_name.to_string();
557        v.push(quote! {let #field_name = form_map.get(#s)?.parse()?;});
558    }
559
560    let expanded = quote! {
561        #[async_trait::async_trait]
562        impl<B: anansi::web::BaseRequest + anansi::web::GetRecord> anansi::forms::GetData<B> for #name {
563            fn from_map(form_map: anansi::web::FormMap) -> anansi::web::Result<<Self as anansi::forms::Form>::Data> {
564                #(#v)*
565                Ok(<Self as anansi::forms::Form>::Data::new(#(#field_names,)*))
566            }
567            async fn from_record(record: <Self as anansi::forms::HasRecord>::Item, req: &B) -> anansi::web::Result<<Self as anansi::forms::Form>::Data> {
568                Ok(<Self as anansi::forms::Form>::Data::new(#(record.#field_names,)*))
569            }
570        }
571    };
572
573    proc_macro::TokenStream::from(expanded)
574}
575
576#[proc_macro_derive(ToEdit)]
577pub fn to_edit_macro_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
578    let input = parse_macro_input!(input as DeriveInput);
579    let name = input.ident;
580    let field_names = get_names(&input.data);
581
582    let expanded = quote! {
583        #[async_trait::async_trait]
584        impl <B: anansi::web::BaseRequest + anansi::web::GetRecord> anansi::forms::ToEdit<B> for #name {
585            async fn on_post(&mut self, data: <Self as Form>::Data, req: &B) -> anansi::web::Result<Self::Item> {
586                let mut record: Self::Item = req.get_record().await?;
587                #(record.#field_names = data.#field_names;)*
588                record.update(req).await?;
589                Ok(record)
590            }
591        }
592    };
593
594    proc_macro::TokenStream::from(expanded)
595}
596
597#[proc_macro_derive(Relate)]
598pub fn relate_macro_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
599    let input = parse_macro_input!(input as DeriveInput);
600    let name = input.ident;
601    let record_tuple = format_ident!("{}Tuple", name);
602    let lower = format_ident!("{}", record_tuple.to_string().to_lowercase());
603
604    let expanded = quote! {
605        impl #name {
606            pub async fn owner<B: anansi::web::BaseRequest>(req: &B) -> anansi::web::Result<()> {
607                use anansi::records::{Record, RecordTuple, FromParams, Text};
608                #record_tuple::check(Text::from(Self::table_name()), Self::pk_from_params(req.params())?, Text::from("owner".to_string()), req).await
609            }
610        }
611
612        #[async_trait::async_trait]
613        impl<R: anansi::web::BaseRequest> anansi::records::Relate<R> for #name {
614            async fn on_save(&self, req: &mut R) -> anansi::web::Result<()> {
615                use anansi::records::Record;
616                #record_tuple::_new(anansi::records::Text::from("auth_user".to_string()), req.user().pk(), None, self.pk(), anansi::records::Text::from("owner".to_string())).save(req).await?;
617                Ok(())
618            }
619            async fn on_delete(&self, req: &R) -> anansi::web::Result<()> {
620                use anansi::records::Record;
621                #record_tuple::delete_whose(#lower::object_key().eq(self.pk())).execute(req).await
622            }
623        }
624    };
625
626    proc_macro::TokenStream::from(expanded)
627}
628
629#[proc_macro_derive(ToDestroy)]
630pub fn to_destroy_macro_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
631    let input = parse_macro_input!(input as DeriveInput);
632    let name = input.ident;
633    let expanded = quote! {
634        #[async_trait::async_trait]
635        impl<R: crate::project::Request> anansi::forms::ToDestroy<R> for #name {}
636    };
637
638    proc_macro::TokenStream::from(expanded)
639}
640
641#[proc_macro_derive(FromParams)]
642pub fn from_params_macro_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
643    let input = parse_macro_input!(input as DeriveInput);
644    let name = input.ident;
645    let param = format!("{}_id", name.to_string().to_lowercase());
646    let expanded = quote! {
647        #[async_trait::async_trait]
648        impl anansi::records::FromParams for #name {
649            fn pk_from_params(params: &anansi::web::Parameters) -> anansi::web::Result<anansi::records::BigInt> {
650                anansi::humanize::decode(params.get(#param)?)
651            }
652            async fn from_params(params: &anansi::web::Parameters) -> anansi::web::Result<anansi::db::Whose<Self>> {
653                let id = Self::pk_from_params(params)?;
654                use anansi::records::Record;
655                Ok(Self::find(id))
656            }
657        }
658    };
659    proc_macro::TokenStream::from(expanded)
660}
661
662#[proc_macro_derive(ToUrl)]
663pub fn to_url_macro_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
664    let input = parse_macro_input!(input as DeriveInput);
665    let name = input.ident;
666    let expanded = quote! {
667        impl anansi::records::ToUrl for #name {
668            fn to_url(&self) -> String {
669                anansi::humanize::encode(self.id)
670            }
671        }
672    };
673    proc_macro::TokenStream::from(expanded)
674}
675
676#[proc_macro_derive(Form, attributes(field))]
677pub fn form_macro_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
678    let input = parse_macro_input!(input as DeriveInput);
679    let name = input.ident;
680    let mut fv = Vec::new();
681    let mut fv2 = Vec::new();
682    let mut data_members = Vec::new();
683    let mut members = Vec::new();
684    let mut members2 = Vec::new();
685    let mut member_names = Vec::new();
686    let mut member_types = Vec::new();
687    let init = form_init(&input.data, &mut data_members, &mut members, &mut members2, &mut member_names, &mut member_types, &mut fv, &mut fv2);
688    let name_strings: Vec<String> = member_names.iter().map(|n| n.to_string()).collect();
689    let record_data = format_ident!("{}Data", name);
690    let record_fields = format_ident!("{}Fields", name);
691    let expanded = quote! {
692        #[derive(Clone)]
693        pub struct #record_data {
694            #init
695        }
696        impl #record_data {
697            pub fn new(#(#data_members,)*) -> Self {
698                Self{#(#member_names,)*}
699            }
700        }
701        #[async_trait::async_trait]
702        impl anansi::forms::Form for #name {
703            type Data = #record_data;
704            fn new() -> Self {
705                Self {
706                    csrf_token: None,
707                    attrs: anansi::forms::Attributes::new(),
708                    data: None,
709                    errors: anansi::forms::FormErrors::new(),
710                    #(#fv)*
711                }
712            }
713            fn csrf_token(&self) -> Option<&String> {
714                self.csrf_token.as_ref()
715            }
716            fn attrs(&self) -> &anansi::forms::Attributes {
717                &self.attrs
718            }
719            fn insert_attr(mut self, key: &str, value: &str) -> Self {
720                self.attrs.insert(key, value);
721                self
722            }
723            fn post(&mut self, token: &anansi::web::TokenRef) {
724                self.csrf_token = Some(format!("{}", token));
725            }
726            async fn from_data(data: Self::Data) -> Self {
727                Self {
728                    csrf_token: None,
729                    attrs: anansi::forms::Attributes::new(),
730                    data: Some(data),
731                    errors: anansi::forms::FormErrors::new(),
732                    #(#fv)*
733                }
734            }
735            fn from_get<B: anansi::web::BaseRequest>(req: &mut B) -> anansi::web::Result<Self> {
736                let mut form_data = req.params_mut();
737                let data = #record_data {
738                    #(#fv2)*
739                };
740                Ok(Self {
741                    csrf_token: None,
742                    attrs: anansi::forms::Attributes::new(),
743                    data: Some(data),
744                    errors: anansi::forms::FormErrors::new(),
745                    #(#fv)*
746                })
747            }
748            fn from_post<B: anansi::web::BaseRequest + anansi::web::CsrfDefense>(req: &mut B) -> anansi::web::Result<Self> {
749                use anansi::web::CsrfDefense;
750                let mut form_data = req.check_token()?;
751                let data = #record_data {
752                    #(#fv2)*
753                };
754                Ok(Self {
755                    csrf_token: None,
756                    attrs: anansi::forms::Attributes::new(),
757                    data: Some(data),
758                    errors: anansi::forms::FormErrors::new(),
759                    #(#fv)*
760                })
761            }
762            fn fill(&mut self) -> anansi::web::Result<()> {
763                use anansi::forms::Field;
764                if let Some(data) = self.data.as_ref() {
765                    #(#members2)*
766                    Ok(())
767                } else {
768                    Err(anansi::web::WebErrorKind::BadFill.to_box())
769                }
770            }
771            fn validate(&mut self) -> anansi::web::Result<#record_data> {
772                if let Some(data) = self.data.take() {
773                    Ok(data)
774                } else {
775                    Err(anansi::web::WebErrorKind::BadValidate.to_box())
776                }
777            }
778            fn errors(&self) -> &anansi::forms::FormErrors {
779                &self.errors
780            }
781            fn add_error(&mut self, e: Box<dyn std::error::Error + Send + Sync>) {
782                self.errors.add_error(e);
783            }
784            fn set_data(&mut self, data: Option<#record_data>) {
785                self.data = data
786            }
787            fn field_names() -> &'static [&'static str] {
788                &[#(#name_strings),*]
789            }
790            fn field(&self, n: usize) -> Option<&dyn anansi::forms::Field> {
791                match n {
792                    #(#members)*
793                    _ => None,
794                }
795            }
796        }
797        impl<'a> IntoIterator for &'a #name {
798            type Item = &'a dyn anansi::forms::Field;
799            type IntoIter = #record_fields<'a>;
800
801            fn into_iter(self) -> #record_fields<'a> {
802                #record_fields {counter: 0, form: self}
803            }
804        }
805        impl #name {
806            pub fn fields(&self) -> #record_fields {
807                #record_fields {counter: 0, form: self}
808            }
809            pub fn check_field_errors(&self) -> anansi::web::Result<()> {
810                for field in self {
811                    if !field.errors().is_empty() {
812                        return Err(anansi::web::WebErrorKind::FieldError.to_box());
813                    }
814                }
815                Ok(())
816            }
817        }
818        pub struct #record_fields<'a> {
819            counter: usize,
820            form: &'a #name,
821        }
822        impl<'a> Iterator for #record_fields<'a> {
823            type Item = &'a dyn anansi::forms::Field;
824            fn next(&mut self) -> Option<Self::Item> {
825                use anansi::forms::Form;
826                let field = self.form.field(self.counter);
827                self.counter += 1;
828                field
829            }
830        }
831    };
832    proc_macro::TokenStream::from(expanded)
833}
834
835#[proc_macro_attribute]
836pub fn form(args: proc_macro::TokenStream, input: proc_macro::TokenStream) -> proc_macro::TokenStream {
837    let args = parse_macro_input!(args as SchemaArgs);
838    let input = parse_macro_input!(input as DeriveInput);
839    let attrs = &input.attrs;
840    let name = input.ident;
841    let mut v = Vec::new();
842    let has_record = if !args.vars.is_empty() {
843        let item = &args.vars[0];
844        quote! {
845            impl anansi::forms::HasRecord for #name {
846                type Item = #item;
847            }
848        }
849    } else {
850        quote! {}
851    };
852    let record_data = format_ident!("{}Data", name);
853    v.push(quote! {csrf_token: Option<String>});
854    v.push(quote! {attrs: anansi::forms::Attributes});
855    v.push(quote! {data: Option<#record_data>});
856    v.push(quote! {errors: anansi::forms::FormErrors});
857    match &input.data {
858        Struct(data_struct) => {
859            match &data_struct.fields {
860                Fields::Named(named) => {
861                    for f in &named.named {
862                        v.push(quote!{#f});
863                    }
864                },
865                _ => unimplemented!(),
866            }
867        },
868        _ => unimplemented!(),
869    }
870    let q = quote! {
871        #[derive(anansi::Form)]
872        #(#attrs)*
873        pub struct #name {
874            #(#v),*
875        }
876        #has_record
877    };
878    q.into()
879}
880
881fn expr_attrs(attrs: &Vec<Expr>) -> HashMap<String, String> {
882    let mut hm = HashMap::new();
883    for attr in attrs {
884        if let Expr::Assign(assign) = attr {
885            if let Expr::Path(expr_path) = &*assign.left {
886                if expr_path.path.segments[0].ident.to_owned() == "table_name" {
887                    let tokens = &assign.right;
888                    let tokens = quote! {#tokens}.to_string();
889                    hm.insert("table_name".to_owned(), tokens[1..tokens.len()-1].to_owned());
890                    break;
891                }
892            }
893        }
894        panic!("unexpected argument in macro attribute")
895    }
896    hm
897}
898
899fn get_attrs(attrs: &Vec<Attribute>) -> HashMap<String, String> {
900    let mut hm = HashMap::new();
901    for attr in attrs {
902        if attr.path.segments[0].ident.to_owned() == "field" {
903            let tokens = &attr.tokens;
904            let tokens = quote! {#tokens}.to_string();
905            let args = tokens[1..tokens.len()-1].split(',');
906            for arg in args {
907                let (key, value) = arg.split_once('=').unwrap();
908                let value = value.trim();
909                hm.insert(key.trim().to_owned(), value[1..value.len()-1].to_owned());
910            }
911            break;
912        }
913    }
914    hm
915}
916
917fn form_init(data: &Data, data_members: &mut Vec<TokenStream>, members: &mut Vec<TokenStream>, members2: &mut Vec<TokenStream>, member_names: &mut Vec<Ident>, member_types: &mut Vec<TokenStream>, fv: &mut Vec<TokenStream>, fv2: &mut Vec<TokenStream>) -> TokenStream {
918    match *data {
919        Data::Struct(ref data) => {
920            match data.fields {
921                Fields::Named(ref fields) => {
922                    let mut n: usize = 0;
923                    let recurse = fields.named.iter().skip(4).map(|f| {
924                        let name = &f.ident;
925                        let ns = name.as_ref().unwrap().to_string();
926                        let attrs = get_attrs(&f.attrs);
927                        let label = if let Some(label) = attrs.get("label") {
928                            let label = &label[1..label.len()-1];
929                            quote! {#label}
930                        } else {
931                            let mut s = String::new();
932                            let mut n = 0;
933                            for c in ns.chars() {
934                                if n > 0 {
935                                    if c != '_' {
936                                        s.push(c);
937                                    } else {
938                                        s.push(' ');
939                                    }
940                                } else {
941                                    s.push_str(&c.to_uppercase().to_string());
942                                }
943                                n += 1;
944                            }
945                            quote! {#s}
946                        };
947                        let widget = if let Some(widget) = attrs.get("widget") {
948                            let w = format_ident!("{}", widget);
949                            quote! {anansi::forms::#w}
950                        } else {
951                            quote! {anansi::forms::TextInput}
952                        };
953                        let ty = &f.ty;
954                        let required = if let Some(required) = attrs.get("required") {
955                            if required == "false" {
956                                false
957                            } else {
958                                panic!("unexpected value for `required` attribute");
959                            }
960                        } else {
961                            true
962                        };
963                        let q = if required {
964                            quote! {
965                                #name: <#ty>::new(#label, Box::new(#widget {name: #ns, attrs: anansi::forms::Attributes::new().id(#ns).pass("required", "")})),
966                            }
967                        } else {
968                            quote! {
969                                #name: <#ty>::new(#label, Box::new(#widget {name: #ns, attrs: anansi::forms::Attributes::new().id(#ns)})),
970                            }
971                        };
972                        let q2 = if required {
973                            quote! {
974                                #name: {
975                                    let s = form_data.remove(#ns)?;
976                                    if !s.is_empty() {
977                                        s.parse()?
978                                    } else {
979                                        return Err(anansi::web::WebErrorKind::BadField.to_box());
980                                    }
981                                },
982                            }
983                        } else {
984                            quote! {
985                                #name: {
986                                    match form_data.remove(#ns) {
987                                        Ok(s) => {
988                                            if !s.is_empty() {
989                                                Some(<anansi::records::#ty as anansi::records::DataType>::from_val(s)?)
990                                            } else {
991                                                None
992                                            }
993                                        },
994                                        Err(_) => None,
995                                    }
996                                },
997                            }
998                        };
999                        let q3 = quote! {
1000                            #n => Some(&self.#name as &dyn anansi::forms::Field),
1001                        };
1002                        let q4 = if required {
1003                            quote! {
1004                                self.#name.mut_widget().mut_attrs().insert("value", &anansi::web::html_escape(&format!("{}", data.#name)));
1005                            }
1006                        } else {
1007                            quote! {
1008                                self.#name.mut_widget().mut_attrs().insert("value", &anansi::web::html_escape(&match data.#name {Some(ref f) => format!("{}", f), None => "".to_string(),}));
1009                            }
1010                        };
1011                        n += 1;
1012                        fv.push(q);
1013                        fv2.push(q2);
1014                        if required {
1015                            data_members.push(quote! {#name: anansi::records::#ty});
1016                        } else {
1017                            data_members.push(quote! {#name: Option<anansi::records::#ty>});
1018                        };
1019                        members.push(q3);
1020                        members2.push(q4);
1021                        member_names.push(name.clone().unwrap());
1022                        if required {
1023                            member_types.push(quote!{#ty});
1024                            quote_spanned! {f.span() =>
1025                                pub #name: anansi::records::#ty,
1026                            }    
1027                        } else {
1028                            member_types.push(quote!{Option<#ty>});
1029                            quote_spanned! {f.span() =>
1030                                pub #name: Option<anansi::records::#ty>,
1031                            }
1032                        }
1033                        
1034                    });
1035                    quote! {
1036                        #(#recurse)*
1037                    }
1038                },
1039                _ => unimplemented!(),
1040            }
1041        },
1042        _ => unimplemented!(),
1043    }
1044}
1045
1046fn ty_string(ty: &Type, segment: &String) -> String {
1047    let ty = quote! {#ty}.to_string();
1048    let t = ty[segment.len()+3..ty.len()-2].to_string();
1049    t
1050}
1051
1052fn record_init(mname: &Ident, fname: &str, data: &Data, pkd: &mut PkData, members: &mut Vec<String>, fv: &mut Vec<TokenStream>, fv2: &mut Vec<TokenStream>) -> TokenStream {
1053    match *data {
1054        Data::Struct(ref data) => {
1055            match data.fields {
1056                Fields::Named(ref fields) => {
1057                    let recurse = fields.named.iter().map(|f| {
1058                        let name = &f.ident;
1059                        let member = name.as_ref().unwrap().to_string();
1060                        let m2 = member.to_ascii_lowercase();
1061                        let column = quote! {format!("{}.{}", #mname::table(), #member)};
1062                        let lowcolumn = quote! {&format!("{}.{}", super::#mname::table(), #member)};
1063                        let attrs = get_attrs(&f.attrs);
1064                        let mut fty = f.ty.clone();
1065                        match &f.ty {
1066                            Path(path) => {
1067                                let mut segment = path.path.segments.last().unwrap().ident.to_string();
1068                                let mut null = false;
1069                                
1070                                let mut is_pk = false;
1071                                if let Some(pk) = attrs.get("primary_key") {
1072                                    if pkd.name.is_empty() && pk == "true" {
1073                                        is_pk = true;
1074                                        pkd.name = member.clone();
1075                                        pkd.ty = segment.clone();
1076                                        let df = attrs.get("default_fn").expect("expected default function for primary key");
1077                                        let df: proc_macro2::TokenStream = df.parse().expect("error parsing default function");
1078                                        pkd.values.push(quote! {#name: #df()});
1079                                    } else {
1080                                        panic!("only one primary key permitted");
1081                                    }
1082                                } else if segment != "ManyToMany" {
1083                                    pkd.params.push(quote! {#name: #fty,});
1084                                    pkd.values.push(quote! {#name});
1085                                } else {
1086                                    pkd.values.push(quote! {#name: anansi::records::ManyToMany::new()});
1087                                }
1088                                if segment == "Option" {
1089                                    pkd.member_type.push((true, name.as_ref().unwrap().clone(), quote! {#fty}));
1090                                    null = true;
1091                                    let ty = &f.ty;
1092                                    let ty = quote! {#ty}.to_string();
1093                                    let mut s = ty[segment.len()+3..ty.len()-2].to_string();
1094                                    s = match s.rsplit_once("::") {
1095                                        Some((_, second)) => second.to_string(),
1096                                        None => s,
1097                                    };
1098                                    match s.rsplit_once('<') {
1099                                        Some((first, _)) => {
1100                                            segment = first.trim().to_string()
1101                                        },
1102                                        None => {
1103                                            segment = s.trim().to_string()
1104                                        },
1105                                    }
1106                                    let (t, u) = ty.rsplit_once("Option <").unwrap();
1107                                    let (u, _) = u.rsplit_once('>').unwrap();
1108                                    let v: syn::Type = syn::parse_str(&format!("{}{}", t, u)).unwrap();
1109                                    fty = v;
1110                                } else {
1111                                    pkd.member_type.push((false, name.as_ref().unwrap().clone(), quote! {#fty}));
1112                                }
1113                                match segment.as_str() {
1114                                    "ManyToMany" => {
1115                                        let ty = &f.ty;
1116                                        let ty = quote! {#ty}.to_string();
1117                                        let ty = &ty[segment.len()+3..ty.len()-2];
1118                                        let lower = ty.to_lowercase();
1119                                        let mfield = format_ident!("{}Fields", ty);
1120                                        let q = quote! {pub fn #name() -> #mfield<#mname> {let full_name = format!("{}_{}", super::super::APP_NAME, #fname); let join = format!("{}_{}", full_name, #lower); #mfield::new(anansi::db::Builder::new().inner_join(&join, &full_name, "id", &format!("{}_id", #fname)).inner_join(&format!("{}_{}", super::super::APP_NAME, #lower), &join, &format!("{}_id", #lower), "id"))}};
1121                                        let q2 = quote! {pub fn #name(self) -> #mfield<F> {let full_name = format!("{}_{}", super::APP_NAME, #fname); let join = format!("{}_{}", full_name, #lower); #mfield::new(anansi::db::Builder::new().inner_join(&join, &full_name, "id", &format!("{}_id", #fname)).inner_join(&format!("{}_{}", super::APP_NAME, #lower), &join, &format!("{}_id", #lower), "id"))}};
1122                                        fv.push(q);
1123                                        fv2.push(q2);
1124                                        quote_spanned! {f.span() =>
1125                                            #name: anansi::records::ManyToMany::new(),
1126                                        }
1127                                    },
1128                                    "BigInt" => {
1129                                        let q = quote! {pub fn #name() -> anansi::db::Column<#mname, anansi::records::BigInt> {anansi::db::Column::new(#lowcolumn)}};
1130                                        let q2 = quote! {pub fn #name(self) -> anansi::db::Column<F, anansi::records::BigInt> {anansi::db::Column::from(self.b.push_val(anansi::db::Clause::Column(#column)))}};
1131                                        fv.push(q);
1132                                        if is_pk {
1133                                            let q3 = quote! {pub fn pk() -> anansi::db::Column<#mname, anansi::records::BigInt> {anansi::db::Column::new(#lowcolumn)}};
1134                                            fv.push(q3);
1135                                        }
1136                                        fv2.push(q2);
1137                                        members.push(member);
1138                                        quote_spanned! {f.span() =>
1139                                            #name: <anansi::records::BigInt as anansi::records::DataType>::from_val(row.try_i64(#m2)?)?,
1140                                        }
1141                                    },
1142                                    "Int" => {
1143                                        let q = quote! {pub fn #name() -> anansi::db::Column<#mname, anansi::records::Int> {anansi::db::Column::new(#lowcolumn)}};
1144                                        let q2 = quote! {pub fn #name(self) -> anansi::db::Column<F, anansi::records::Int> {anansi::db::Column::from(self.b.push_val(anansi::db::Clause::Column(#column)))}};
1145                                        fv.push(q);
1146                                        if is_pk {
1147                                            let q3 = quote! {pub fn pk() -> anansi::db::Column<#mname, anansi::records::Int> {anansi::db::Column::new(#lowcolumn)}};
1148                                            fv.push(q3);
1149                                        }
1150                                        fv2.push(q2);
1151                                        members.push(member);
1152                                        quote_spanned! {f.span() =>
1153                                            #name: <anansi::records::Int as anansi::records::DataType>::from_val(row.try_i32(#m2)?)?,
1154                                        }
1155                                    },
1156                                    "ForeignKey" => {
1157                                        if pkd.ty == "BigInt" {
1158                                            let q = quote! {pub fn #name() -> anansi::db::Column<#mname, anansi::records::BigInt> {anansi::db::Column::new(#lowcolumn)}};
1159                                            let q2 = quote! {pub fn #name(self) -> anansi::db::Column<F, anansi::records::BigInt> {anansi::db::Column::from(self.b.push_val(anansi::db::Clause::Column(#column)))}};
1160
1161                                            fv.push(q);
1162                                            fv2.push(q2);
1163                                            members.push(member);
1164                                            let mut ts = ty_string(&fty, &segment);
1165                                            if let Some(t) = ts.split_once(',') {
1166                                                ts = t.0.trim().to_string();
1167                                            }
1168                                            pkd.fkv.push((name.as_ref().unwrap().clone(), ts));
1169                                            let qs = if !null {
1170                                                quote_spanned! {f.span() =>
1171                                                    #name: <anansi::records::#fty as anansi::records::DataType>::from_val(row.try_i64(#m2)?)?,
1172                                                }
1173                                            } else {
1174                                                quote_spanned! {f.span() =>
1175                                                    #name: {
1176                                                        let o = row.try_i64(#m2);
1177                                                        if let Ok(n) = o {
1178                                                            Some(<anansi::records::#fty as anansi::records::DataType>::from_val(n)?)
1179                                                        } else {
1180                                                            None
1181                                                        }
1182                                                    }
1183                                                }
1184                                            };
1185                                            qs
1186                                        } else {
1187                                            panic!("unexpected primary key type for foreign key");
1188                                        }
1189                                    },
1190                                    "DateTime" => {
1191                                        let q = quote! {pub fn #name<'a>() -> anansi::db::Column<#mname, anansi::records::DateTime> {anansi::db::Column::new(#lowcolumn)}};
1192                                        let q2 = quote! {pub fn #name<'a>(self) -> anansi::db::Column<F, anansi::records::DateTime> {anansi::db::Column::from(self.b.push_val(anansi::db::Clause::Column(#column)))}};
1193                                        fv.push(q);
1194                                        fv2.push(q2);
1195                                        members.push(member);
1196                                        quote_spanned! {f.span() =>
1197                                            #name: <anansi::records::DateTime as anansi::records::DataType>::from_val(row.try_date_time(#m2)?)?,
1198                                        }
1199                                    },
1200                                    "Boolean" => {
1201                                        let q = quote! {pub fn #name() -> anansi::db::Column<#mname, anansi::records::Boolean> {anansi::db::Column::new(#lowcolumn)}};
1202                                        let q2 = quote! {pub fn #name(self) -> anansi::db::Column<F, anansi::records::Boolean> {anansi::db::Column::from(self.b.push_val(anansi::db::Clause::Column(#column)))}};
1203                                        fv.push(q);
1204                                        fv2.push(q2);
1205                                        members.push(member);
1206                                        quote_spanned! {f.span() =>
1207                                            #name: <anansi::records::Boolean as anansi::records::DataType>::from_val(row.try_bool(#m2)?)?,
1208                                        }
1209                                    },
1210                                    "VarChar" => {
1211                                        let q = quote! {pub fn #name<'a>() -> anansi::db::Column<#mname, anansi::records::#fty> {anansi::db::Column::new(#lowcolumn)}};
1212                                        let q2 = quote! {pub fn #name<'a>(self) -> anansi::db::Column<F, anansi::records::#fty> {anansi::db::Column::from(self.b.push_val(anansi::db::Clause::Column(#column)))}};
1213                                        fv.push(q);
1214                                        fv2.push(q2);
1215                                        members.push(member);
1216                                        if !null {
1217                                            quote_spanned! {f.span() =>
1218                                                #name: <anansi::records::#fty as anansi::records::DataType>::from_val(row.try_string(#m2)?)?,
1219                                            }
1220                                        } else {
1221                                            quote_spanned! {f.span() =>
1222                                                #name: {
1223                                                    let o = row.try_string(#m2);
1224                                                    if let Ok(s) = o {
1225                                                        Some(<anansi::records::#fty as anansi::records::DataType>::from_val(s)?)
1226                                                    } else {
1227                                                        None
1228                                                    }
1229                                                }
1230                                                ,
1231                                            }
1232                                        }
1233                                    },
1234                                    "Text" => {
1235                                        let q = quote! {pub fn #name<'a>() -> anansi::db::Column<#mname, anansi::records::Text> {anansi::db::Column::new(#lowcolumn)}};
1236                                        let q2 = quote! {pub fn #name<'a>(self) -> anansi::db::Column<F, anansi::records::Text> {anansi::db::Column::from(self.b.push_val(anansi::db::Clause::Column(#column)))}};
1237                                        fv.push(q);
1238                                        fv2.push(q2);
1239                                        members.push(member);
1240                                        if !null {
1241                                            quote_spanned! {f.span() =>
1242                                                #name: anansi::records::Text::from(row.try_string(#m2)?),
1243                                            }
1244                                        } else {
1245                                            quote_spanned! {f.span() =>
1246                                                #name: {
1247                                                    let o = row.try_option_string(#m2)?;
1248                                                    if let Some(s) = o {
1249                                                        Some(anansi::records::Text::from(s))
1250                                                    } else {
1251                                                        None
1252                                                    }
1253                                                },
1254                                            }
1255                                        }
1256                                    },
1257                                    _ => {
1258                                        panic!("{}", segment.as_str());
1259                                    },
1260                                }
1261                            },
1262                            _ => unimplemented!(),
1263                        }
1264                    });
1265                    quote! {
1266                        #(#recurse)*
1267                    }
1268                },
1269                _ => unimplemented!(),
1270            }
1271        },
1272        _ => unimplemented!(),
1273    }
1274}
1275
1276#[proc_macro]
1277pub fn app_components(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
1278    let input = parse_macro_input!(input as SchemaArgs);
1279    let comps = &input.vars;
1280    let q = quote! {
1281        pub const COMPONENTS: &'static [anansi_aux::Mounts] = &[#(#comps::CB,)*];
1282    };
1283    q.into()
1284}
1285
1286#[cfg(not(target_os = "windows"))]
1287macro_rules! main_separator {
1288    () => {r"/"}
1289}
1290
1291#[cfg(target_os = "windows")]
1292macro_rules! main_separator {
1293    () => {r"\"}
1294}
1295
1296#[proc_macro]
1297pub fn start(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
1298    let input = parse_macro_input!(input as SchemaArgs);
1299    let comps = &input.vars[0];
1300 
1301    let q = quote! {
1302        #[wasm_bindgen::prelude::wasm_bindgen]
1303        pub fn start() {
1304            let mut callbacks = std::collections::HashMap::new();
1305            for comp in #comps::COMPONENTS {
1306                for (name, new, call) in *comp {
1307                    callbacks.insert(name.to_string(), anansi_aux::CallbackData {new: *new, call: *call});
1308                }
1309            }
1310            anansi_aux::setup(callbacks);
1311        }
1312    };
1313    q.into()
1314}
1315
1316#[proc_macro]
1317pub fn wasm_statics(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
1318    let input = parse_macro_input!(input as SchemaArgs);
1319    let var = &input.vars[0];
1320    let name = quote! {#var}.to_string();
1321    let name = name.replace('"', "");
1322    let under_name = name.replace("-", "_");
1323    let name_js = format!("{}.js", under_name);
1324    let name_wasm = format!("{}_bg.wasm", under_name);
1325    let pkg = format!("..{}{}{0}pkg{0}", main_separator!(), name);
1326    let main = format!("..{}{}{0}main.js", main_separator!(), name);
1327    let sw = format!("..{}{}{0}sw.js", main_separator!(), name);
1328    let stat = "/static/pkg/";
1329    let names = quote! {
1330        &[
1331            ("/static/main.js", include_bytes!(#main)),
1332            ("/static/sw.js", include_bytes!(#sw)),
1333            (concat!(#stat, #name_js), include_bytes!(concat!(#pkg, #name_js))),
1334            (concat!(#stat, #name_wasm), include_bytes!(concat!(#pkg, #name_wasm)))
1335        ]
1336    };
1337    names.into()
1338}
1339
1340#[proc_macro]
1341pub fn raw_middleware(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
1342    let input = parse_macro_input!(input as SchemaArgs);
1343    let _vars = input.vars;
1344
1345    let args = quote! {<anansi::web::SecurityHeaders<anansi::web::ViewService> as Service<B>>::init(vs, settings).await};
1346    let retval = quote! {anansi::web::SecurityHeaders<anansi::web::ViewService>};
1347
1348    let q = quote! {
1349        pub fn app_services<B: anansi::web::BaseRequest>(settings: &anansi::server::Settings) -> std::pin::Pin<Box<dyn std::future::Future<Output = #retval> + Send + '_>> {
1350            use anansi::web::Service;
1351            let vs = anansi::web::ViewService;
1352            Box::pin(async {#args})
1353        }
1354    }.into();
1355    q
1356}
1357
1358#[proc_macro]
1359pub fn middleware(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
1360    let input = parse_macro_input!(input as SchemaArgs);
1361
1362    let mut args = quote! {<anansi::web::SecurityHeaders<anansi::web::ViewService> as Service<B>>::init(vs, settings).await};
1363    let retval = if input.vars.is_empty() {
1364        quote! {anansi::web::SecurityHeaders<anansi::web::ViewService>}
1365    } else {
1366        let last = input.vars.last().unwrap();
1367        quote! {#last}
1368    };
1369    for arg in input.vars {
1370        args = quote!{#arg::init(#args, settings).await}
1371    }
1372    let q = quote! {
1373        pub fn app_services<B: anansi::web::BaseRequest>(settings: &anansi::server::Settings) -> std::pin::Pin<Box<dyn std::future::Future<Output = #retval> + Send + '_>> {
1374            use anansi::web::Service;
1375            let vs = anansi::web::ViewService;
1376            Box::pin(async {#args})
1377        }
1378        anansi::setup!();
1379    }.into();
1380    q
1381}
1382
1383#[proc_macro]
1384pub fn app_statics(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
1385    let input = parse_macro_input!(input as SchemaArgs);
1386    let mut args = vec![];
1387    for arg in input.vars {
1388        if let syn::Expr::Macro(a) = arg {
1389            args.push(quote! {#a});
1390        } else {
1391            args.push(quote! {#arg::STATICS});
1392        }
1393    }
1394    let q = quote! {
1395        static APP_STATICS: &[&[anansi::web::Static]] = &[
1396            #(#args),*
1397        ];
1398    }.into();
1399    q
1400}
1401
1402#[proc_macro_attribute]
1403pub fn base_view(_args: proc_macro::TokenStream, input: proc_macro::TokenStream) -> proc_macro::TokenStream {
1404    let input = parse_macro_input!(input as ItemFn);
1405    let sig = &input.sig;
1406    let generics = &sig.generics;
1407    let fname = &sig.ident;
1408    let fnargs = &sig.inputs;
1409    let name = String::from(concat!(main_separator!(), ".parsed", main_separator!())) + &sig.ident.to_string() + ".in";
1410    let args_name = format!("{}.parsed{0}", main_separator!()) + &sig.ident.to_string() + "_args.in";
1411    let stmts = input.block.stmts;
1412    let rty = match &input.sig.output {
1413        syn::ReturnType::Type(_, ty) => match &**ty {
1414            Type::Path(path) => {
1415                &path.path.segments
1416            },
1417            _ => panic!("Could not get path"),
1418        },
1419        _ => panic!("Could not get return type"),
1420    };
1421    let q = quote! {
1422        pub mod #fname {
1423            use super::*;
1424            include!(concat!("templates", #args_name));
1425            pub fn base #generics (#fnargs, _base_args: Args) -> #rty {
1426                #(#stmts)*
1427                Ok(Response::new(200, include!(concat!("templates", #name))))
1428            }
1429        }
1430    };
1431
1432    q.into()
1433}
1434
1435#[proc_macro_attribute]
1436pub fn cacheable(_args: proc_macro::TokenStream, input: proc_macro::TokenStream) -> proc_macro::TokenStream {
1437    let input = parse_macro_input!(input as ItemFn);
1438    let attrs = input.attrs;
1439    let vis = input.vis;
1440    let sig = input.sig;
1441    let (req, ty) = if let syn::FnArg::Typed(pat_type) = sig.inputs.first().unwrap() {
1442        (pat_type.pat.clone(), pat_type.ty.clone())
1443    } else {
1444        panic!("expected typed argument for cached view");
1445    };
1446    let ret = sig.output;
1447    let sig_ident = &sig.ident;
1448    let base_ident = format_ident!("_base_{}", sig_ident);
1449    let cache_ident = format_ident!("_cache_{}", sig_ident);
1450    
1451    let name = format!("{}.parsed{0}", main_separator!()) + &sig.ident.to_string() + ".in";
1452
1453    let attr_ident = attrs[0].path.segments.last().unwrap().ident.to_string();
1454    let tokens = &attrs[0].tokens;
1455    let key = if tokens.to_string().contains("Group :: is_visitor") {
1456        quote!{format!("_c{}", Self::#cache_ident::<N> as usize)}
1457    } else {
1458        panic!("expected cache to be set to visitor");
1459    };
1460    let extend = match attr_ident.as_str() {
1461        "view" => quote! {{let mut _args = base::Args::new(); include!(concat!("templates", #name))}},
1462        "check" => quote! {},
1463        _ => panic!("expected `view` or `check` macro"),
1464    };
1465    let stmts = input.block.stmts;
1466    let q = quote! {
1467        #[anansi::check(#tokens)]
1468        #vis async fn #cache_ident<const N: usize>(#req: #ty) #ret {
1469            let key = #key;
1470            let _b = if let Ok(v) = #req.cache().get(&key).await {
1471                base::Args::from_bytes(v)?
1472            } else {
1473                let _b = Self::#base_ident(#req).await?;
1474                #req.cache_mut().set_ex(&key, &_b.to_bytes(), Some(N)).await?;
1475                _b
1476            };
1477            base::base(#req, _b)
1478        }
1479        async fn #base_ident(#req: #ty) -> anansi::web::Result<base::Args> {
1480            #(#stmts)*
1481            Ok(#extend)
1482        }
1483        #[anansi::check(#tokens)]
1484        #vis async fn #sig_ident(#req: #ty) #ret {
1485            let _b =  Self::#base_ident(#req).await?;
1486            base::base(#req, _b)
1487        }
1488    };
1489    q.into()
1490}
1491
1492#[proc_macro_attribute]
1493pub fn view(args: proc_macro::TokenStream, input: proc_macro::TokenStream) -> proc_macro::TokenStream {
1494    let input = parse_macro_input!(input as ItemFn);
1495    let args = parse_macro_input!(args as SchemaArgs);
1496    let vars = args.vars;
1497    let vis = input.vis;
1498    let sig = input.sig;
1499    let name = &sig.ident.to_string();
1500
1501    let stmts = input.block.stmts;
1502    let q = quote! {
1503        #[anansi::check(#(#vars)*)]
1504        #vis #sig {
1505            #(#stmts)*
1506            anansi::extend!(req, base, #name)
1507        }
1508    };
1509    q.into()
1510}
1511
1512#[proc_macro_attribute]
1513pub fn check(args: proc_macro::TokenStream, input: proc_macro::TokenStream) -> proc_macro::TokenStream {
1514    let input = parse_macro_input!(input as ItemFn);
1515    let args = parse_macro_input!(args as SchemaArgs);
1516    let vars = &args.vars;
1517    if vars.is_empty() {
1518        panic!("Expected function");
1519    }
1520    let generics = input.sig.generics;
1521    let vis = input.vis;
1522    let sig_ident = input.sig.ident;
1523    let _sig_ident = format_ident!("_{}", sig_ident);
1524    let (req, ty) = match &input.sig.inputs[0] {
1525        Typed(pat) => {
1526            let req = match &*pat.pat {
1527                Pat::Ident(id) => id,
1528                _ => panic!("Could not get request"),
1529            };
1530            let ty = &pat.ty;
1531            let ty = quote! {#ty};
1532            (req, ty)
1533        },
1534        _ => panic!("Not type"),
1535    };
1536    let rty = match &input.sig.output {
1537        syn::ReturnType::Type(_, ty) => match &**ty {
1538            Type::Path(path) => {
1539                &path.path.segments
1540            },
1541            _ => panic!("Could not get path"),
1542        },
1543        _ => panic!("Could not get return type"),
1544    };
1545    let stmts = input.block.stmts;
1546    let mut generic_idents = quote!{};
1547    let where_clause = &generics.where_clause;
1548    let _sig_generic = if generics.lt_token.is_none() {
1549        quote! {#_sig_ident}
1550    } else {
1551        let mut v = vec![];
1552        for param in &generics.params {
1553            match param {
1554                GenericParam::Type(ty) => {
1555                    let t = &ty.ident;
1556                    v.push(quote!{#t});
1557                }
1558                GenericParam::Lifetime(lt) => {
1559                    let l = &lt.lifetime;
1560                    v.push(quote!{#l});
1561                }
1562                GenericParam::Const(co) => {
1563                    let c = &co.ident;
1564                    v.push(quote!{#c});
1565                }
1566            }
1567        }
1568        generic_idents = quote! {::<#(#v,)*>};
1569        quote! {#_sig_ident::#generics}
1570    };
1571    let q = quote! {
1572        async fn #_sig_ident #generics (#req: #ty) -> #rty #where_clause {
1573            #(#vars(#req).await?;)*
1574            #(#stmts)*
1575        }
1576        #vis fn #sig_ident #generics (_raw: #ty) -> std::pin::Pin<Box<dyn std::future::Future<Output = #rty> + Send + '_>> #where_clause {
1577            Box::pin(Self::#_sig_ident #generic_idents(_raw))
1578        }
1579    };
1580    q.into()
1581}
1582
1583#[proc_macro_attribute]
1584pub fn check_fn(args: proc_macro::TokenStream, input: proc_macro::TokenStream) -> proc_macro::TokenStream {
1585    let input = parse_macro_input!(input as ItemFn);
1586    let args = parse_macro_input!(args as Args);
1587    let generics = input.sig.generics;
1588    let vis = input.vis;
1589    let sig_ident = input.sig.ident;
1590    let _sig_ident = format_ident!("_{}", sig_ident);
1591    let (req, ty) = match &input.sig.inputs[0] {
1592        Typed(pat) => {
1593            let req = match &*pat.pat {
1594                Pat::Ident(id) => id,
1595                _ => panic!("Could not get request"),
1596            };
1597            let ty = match &*pat.ty {
1598                Type::Path(path) => {
1599                    &path.path.segments
1600                },
1601                _ => panic!("Could not get type"),
1602            };
1603            (req, ty)
1604        },
1605        _ => panic!("Not type"),
1606    };
1607    let rty = match &input.sig.output {
1608        syn::ReturnType::Type(_, ty) => match &**ty {
1609            Type::Path(path) => {
1610                &path.path.segments
1611            },
1612            _ => panic!("Could not get path"),
1613        },
1614        _ => panic!("Could not get return type"),
1615    };
1616    let func = &args.vars[0];
1617    let stmts = input.block.stmts;
1618    let mut generic_idents = quote!{};
1619    let _sig_generic = if generics.lt_token.is_none() {
1620        quote! {#_sig_ident}
1621    } else {
1622        let mut v = vec![];
1623        for param in &generics.params {
1624            match param {
1625                GenericParam::Type(ty) => v.push(ty.ident.clone()),
1626                _ => unimplemented!(),
1627            }
1628        }
1629        generic_idents = quote! {::<#(#v,)*>};
1630        quote! {#_sig_ident::#generics}
1631    };
1632    let q = quote! {
1633        async fn #_sig_ident #generics (#req: #ty) -> #rty {
1634            #(#stmts)*
1635        }
1636        #vis fn #sig_ident #generics(_raw: #ty) -> std::pin::Pin<Box<dyn std::future::Future<Output = #rty> + Send>> {
1637            Box::pin(#func(_raw, #_sig_ident #generic_idents))
1638        }
1639    };
1640    q.into()
1641}
1642
1643#[proc_macro_attribute]
1644pub fn viewer(_metadata: proc_macro::TokenStream, input: proc_macro::TokenStream) -> proc_macro::TokenStream {
1645    let input = parse_macro_input!(input as ViewerArgs);
1646    let imp = input.imp;
1647    let ty = &imp.self_ty;
1648    let gen = &imp.generics;
1649    let id = match &gen.params[0] {
1650        GenericParam::Type(t) => {
1651            &t.ident
1652        },
1653        _ => unimplemented!(),
1654    };
1655    let q = quote! {
1656        pub struct #ty {g: std::marker::PhantomData<#id>}
1657        #imp
1658    };
1659    q.into()
1660}
1661
1662#[proc_macro_attribute]
1663pub fn refchild(_metadata: proc_macro::TokenStream, input: proc_macro::TokenStream) -> proc_macro::TokenStream {
1664    let s = parse_macro_input!(input as syn::ItemStruct);
1665 
1666    let fields = match &s.fields {
1667        syn::Fields::Named(fields_named) => {
1668            &fields_named.named
1669        }
1670        _ => unimplemented!(),
1671    };
1672    let state = &s.ident;
1673    let _state = format_ident!("_{}", s.ident);
1674    let vis = &s.vis;
1675    let mut tfields = vec![];
1676    let mut names = vec![];
1677    let mut methods = vec![];
1678    let attrs = &s.attrs;
1679    for field in fields {
1680        let name = field.ident.as_ref().unwrap();
1681        let ty = &field.ty;
1682        let name_mut = format_ident!("{}_mut", name);
1683        methods.push(quote! {
1684            pub fn #name(&self) -> &#ty {
1685                &self._item.#name
1686            }
1687            pub fn #name_mut(&mut self) -> &mut #ty {
1688                &mut self._item.#name
1689            }
1690        });
1691        tfields.push(quote! {#name: #ty,});
1692        names.push(name);
1693    }
1694    let c = quote! {
1695        #(#attrs)*
1696        #vis struct #state {
1697            _pos: usize,
1698            _item: #_state,
1699        }
1700
1701        #(#attrs)*
1702        #vis struct #_state {
1703            #fields
1704        }
1705
1706        impl #state {
1707            #(#methods)*
1708            fn child(#(#tfields)*) -> #_state {
1709                #_state {#(#names),*}
1710            }
1711        }
1712
1713        impl anansi_aux::RefChild for #state {
1714            type Item = #_state;
1715            fn new(pos: usize, item: Self::Item) -> Self {
1716                Self {_pos: pos, _item: item}
1717            }
1718            fn pos(&self) -> usize {
1719                self._pos
1720            }
1721            fn pos_mut(&mut self) -> &mut usize {
1722                &mut self._pos
1723            }
1724        }
1725    };
1726    c.into()
1727}
1728
1729#[proc_macro_attribute]
1730pub fn store(_metadata: proc_macro::TokenStream, input: proc_macro::TokenStream) -> proc_macro::TokenStream {
1731    let s = parse_macro_input!(input as syn::ItemStruct);
1732 
1733    let fields = match &s.fields {
1734        syn::Fields::Named(fields_named) => {
1735            &fields_named.named
1736        }
1737        _ => unimplemented!(),
1738    };
1739    let state = &s.ident;
1740    let _state = format_ident!("_{}", s.ident);
1741    let vis = &s.vis;
1742    let mut nfields = vec![];
1743    let mut names = vec![];
1744    let mut methods = vec![];
1745    let mut n: i64 = 1;
1746    let attrs = &s.attrs;
1747    for field in fields {
1748        let name = field.ident.as_ref().unwrap();
1749        let ty = &field.ty;
1750        let upper = format_ident!("{}", name.to_string().to_uppercase());
1751        let name_mut = format_ident!("{}_mut", name);
1752        methods.push(quote! {
1753            pub fn #name(&mut self) -> &#ty {
1754                self._proxy.set(Self::#upper);
1755                &self._state.#name
1756            }
1757            pub fn #name_mut(&mut self) -> &mut #ty {
1758                self._proxy._invalid = true;
1759                &mut self._state.#name
1760            }
1761        });
1762        nfields.push(quote! {#vis const #upper: i64 = #n;});
1763        n *= 2;
1764        names.push(name);
1765    }
1766    let c = quote! {
1767        #(#attrs)*
1768        #vis struct #_state {
1769            #fields
1770        }
1771
1772        #vis struct #state {
1773            _proxy: Proxy,
1774            _state: #_state
1775        }
1776
1777        impl #state {
1778            #(#nfields)*
1779            #(#methods)*
1780            #vis fn resume(store: &mut anansi_aux::AppState, n: usize) -> Self {
1781                if let anansi_aux::Obj::Js(v) = store.objs()[n].clone() {
1782                    let state: #_state = serde_json::from_value(v).unwrap();
1783                    let subs = store.subs_mut().pop().expect("problem getting subs");
1784                    Self {_proxy: Proxy::new(subs), _state: state}
1785                } else {
1786                    panic!("expected JavaScript value")
1787                }
1788            }
1789            #vis fn store(#fields) -> Self {
1790                Self {_proxy: Proxy::new(vec![]), _state: #_state {#(#names),*}}
1791            }
1792            #vis fn get_subs(&self) -> Vec<String> {
1793                self._proxy.get_subs()
1794            }
1795            #vis fn into_inner(self) -> #_state {
1796                self._state
1797            }
1798        }
1799    };
1800    c.into()
1801}
1802
1803#[proc_macro_attribute]
1804pub fn record(metadata: proc_macro::TokenStream, input: proc_macro::TokenStream) -> proc_macro::TokenStream {
1805    let mut ast = parse_macro_input!(input as DeriveInput);
1806    let meta = parse_macro_input!(metadata as SchemaArgs);
1807    match &mut ast.data {
1808        syn::Data::Struct(ref mut struct_data) => {
1809            match &mut struct_data.fields {
1810                syn::Fields::Named(fields) => {
1811                    let mut has_pk = false;
1812                    for field in &fields.named {
1813                        let attrs = get_attrs(&field.attrs);
1814                        if let Some(primary) = attrs.get("primary_key") {
1815                            if primary == "true" {
1816                                has_pk = true;
1817                                break;
1818                            }
1819                        }
1820                    }
1821                    if !has_pk {
1822                        fields.named.insert(0, syn::Field::parse_named.parse2(quote! { #[field(primary_key = "true", default_fn = "anansi::records::generate_id")] id: anansi::records::BigInt}).unwrap());
1823                    }
1824                }
1825                _ => {},
1826            }
1827
1828            let meta_attrs = expr_attrs(&meta.vars);
1829            let lowercase = format!("{}", ast.ident.to_string().to_lowercase());
1830            let t = if let Some(name) = meta_attrs.get("table_name") {
1831                quote! {#name.to_string()}
1832            } else {
1833                quote! {format!("{}_{}", super::APP_NAME, #lowercase)}
1834            };
1835            let ident = &ast.ident;
1836            let table = quote! {
1837                impl #ident {
1838                    fn table() -> String {
1839                        #t
1840                    }
1841                }
1842            };
1843
1844            let record_tuple = format_ident!("{}Tuple", ast.ident);
1845            let record_lower = record_tuple.to_string().to_lowercase();
1846            let tt = quote! {format!("{}_{}", super::APP_NAME, #record_lower)};
1847            let tuple_table = quote! {
1848                impl #record_tuple {
1849                    fn table() -> String {
1850                        #tt
1851                    }
1852                }
1853            };
1854
1855            let lower = format_ident!("{}", record_tuple.to_string().to_lowercase());
1856            let tuple = quote! {
1857                #[derive(anansi::Record, anansi::Builder)]
1858                pub struct #record_tuple {
1859                    #[field(primary_key = "true", default_fn = "anansi::records::generate_id")]
1860                    id: anansi::records::BigInt,
1861                    pub subject_namespace: anansi::records::Text,
1862                    pub subject_key: anansi::records::BigInt,
1863                    pub subject_predicate: Option<anansi::records::Text>,
1864                    pub object_key: anansi::records::BigInt,
1865                    pub object_predicate: anansi::records::Text,
1866                }
1867                impl<B: anansi::web::BaseRequest> anansi::records::Relate<B> for #record_tuple {}
1868                #tuple_table
1869                
1870                #[async_trait::async_trait]
1871                impl<R: anansi::web::BaseRequest> anansi::records::RecordTuple<R> for #record_tuple {
1872                    async fn check(object_namespace: anansi::records::Text, object_key: anansi::records::BigInt, object_predicate: anansi::records::Text, req: &R) -> anansi::web::Result<()> {
1873                        use anansi::records::Record;
1874                        let tuples = Self::whose(#lower::object_key().eq(object_key)).and(#lower::object_predicate().eq(object_predicate)).get_all().query(req).await?;
1875                        for tuple in tuples {
1876                            match tuple.subject_predicate {
1877                                None => {
1878                                    if tuple.subject_namespace == "auth_user" && tuple.subject_key == req.user().pk() {
1879                                        return Ok(());
1880                                    }
1881                                },
1882                                Some(predicate) => {
1883                                    if Self::check(tuple.subject_namespace, tuple.subject_key, predicate, req).await.is_ok() {
1884                                        return Ok(());
1885                                    }
1886                                },
1887                            }
1888                        }
1889                        Err(anansi::web::WebErrorKind::NoPermission.to_box())
1890                    }
1891                }
1892            };
1893            
1894            let q = quote! {
1895                #[derive(anansi::Record, anansi::Builder)]
1896                #ast
1897                #tuple
1898                #table
1899            };
1900            return q.into();
1901        }
1902        _ => panic!("macro has to be used on a struct"),
1903    }
1904}
1905
1906struct CacheArgs {
1907    n: Expr,
1908    view: syn::Path,
1909}
1910
1911impl Parse for CacheArgs {
1912    fn parse(input: ParseStream) -> Result<Self> {
1913        let n = input.parse().unwrap();
1914        let _c: Comma = input.parse().unwrap();
1915        let view = input.parse().unwrap();
1916        Ok(Self {view, n,})
1917    }
1918}
1919
1920struct ViewerArgs {
1921    imp: ItemImpl,
1922}
1923
1924impl Parse for ViewerArgs {
1925    fn parse(input: ParseStream) -> Result<Self> {
1926        Ok(Self {
1927            imp: input.parse().unwrap(),
1928        })
1929    }
1930}
1931
1932struct RecordAdminArgs {
1933    record: Type,
1934    hm: HashMap<String, Expr>,
1935}
1936
1937impl Parse for RecordAdminArgs {
1938    fn parse(input: ParseStream) -> Result<Self> {
1939        let record = input.parse().unwrap();
1940        let _comma = input.parse::<Comma>().unwrap();
1941        let vars = Punctuated::<FieldValue, Token![,]>::parse_terminated(input)?;
1942        let mut hm = HashMap::new();
1943        for v in vars {
1944            let name = if let syn::Member::Named(id) = v.member {
1945                id
1946            } else {
1947                panic!("expected named member");
1948            };
1949            hm.insert(name.to_string(), v.expr);
1950        }
1951        Ok(Self {
1952            record,
1953            hm,
1954        })
1955    }
1956}
1957struct SchemaArgs {
1958    vars: Vec<Expr>,
1959}
1960
1961impl Parse for SchemaArgs {
1962    fn parse(input: ParseStream) -> Result<Self> {
1963        let vars = Punctuated::<Expr, Token![,]>::parse_terminated(input)?;
1964        Ok(Self {
1965            vars: vars.into_iter().collect(),
1966        })
1967    }
1968}
1969
1970struct Args {
1971    vars: Vec<Ident>,
1972}
1973
1974impl Parse for Args {
1975    fn parse(input: ParseStream) -> Result<Self> {
1976        let vars = Punctuated::<Ident, Token![,]>::parse_terminated(input)?;
1977        Ok(Self {
1978            vars: vars.into_iter().collect(),
1979        })
1980    }
1981}
1982
1983struct UrlArgs {
1984    req: Expr,
1985    first: Expr,
1986    exprs: Vec<Expr>,
1987}
1988
1989impl Parse for UrlArgs {
1990    fn parse(stream: ParseStream) -> Result<Self> {
1991        let req = stream.parse().unwrap();
1992        let _comma = stream.parse::<Comma>();
1993        let first = stream.parse().unwrap();
1994        let mut exprs = Vec::new();
1995        while let Ok(_) = stream.parse::<Comma>() {
1996            exprs.push(stream.parse().unwrap());
1997        }
1998        Ok(Self {req, first, exprs})
1999    }
2000}
2001
2002#[proc_macro]
2003pub fn extend(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
2004    let input = parse_macro_input!(input as SchemaArgs);
2005    let req = &input.vars[0];
2006    let base = &input.vars[1];
2007    let name = &input.vars[2];
2008    let q = quote! {
2009        {
2010            let mut _args = #base::Args::new();
2011            _args = include!(concat!("templates", anansi::main_separator!(), ".parsed", anansi::main_separator!(), #name, ".in"));
2012            #base::#base(#req, _args)
2013        }
2014    };
2015    q.into()
2016}
2017
2018#[proc_macro]
2019pub fn cache_view(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
2020    let input = parse_macro_input!(input as CacheArgs);
2021    let mut view = input.view.segments.clone();
2022    let name = if let syn::punctuated::Pair::End(t) = view.pop().unwrap() {
2023        format_ident!("_cache_{}", t.ident)
2024    } else {
2025        unimplemented!();
2026    };
2027    let e = &input.n;
2028    let n = match e {
2029        Expr::Lit(n) => quote! {#n},
2030        _ => quote! {{#e}}
2031    };
2032    let q = quote! {
2033        #view #name::<#n>
2034    };
2035    q.into()
2036}
2037
2038#[proc_macro]
2039pub fn aux_path_literal(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
2040    let input = parse_macro_input!(input as SchemaArgs);
2041    let mut l = 1;
2042    let mut qv = vec![];
2043    for arg in &input.vars {
2044        qv.push(if l >= input.vars.len() {
2045            quote! {#arg}
2046        } else {
2047            quote! {#arg, main_separator!(),}
2048        });
2049        l += 1;
2050    }
2051    quote! {
2052        concat!(#(#qv)*)
2053    }.into()
2054}
2055
2056#[proc_macro]
2057pub fn path_literal(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
2058    let input = parse_macro_input!(input as SchemaArgs);
2059    let mut l = 1;
2060    let mut qv = vec![];
2061    for arg in &input.vars {
2062        qv.push(if l >= input.vars.len() {
2063            quote! {#arg}
2064        } else {
2065            quote! {#arg, anansi::main_separator!(),}
2066        });
2067        l += 1;
2068    }
2069    quote! {
2070        concat!(#(#qv)*)
2071    }.into()
2072}
2073
2074#[proc_macro]
2075pub fn record_admin(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
2076    let mut input = parse_macro_input!(input as RecordAdminArgs);
2077    let hm = &mut input.hm;
2078    let path = input.record;
2079    let ps = quote! {#path}.to_string();
2080    let name = if let Some((_, n)) = ps.rsplit_once("::") {
2081        n.trim().to_string()
2082    } else {
2083        ps.clone()
2084    };
2085    let lower = format_ident!("{}", name.to_lowercase());
2086    let (cra, request) = if !ps.starts_with("auth ::") {
2087        (quote! {anansi}, quote!{crate::project::Request})
2088    } else {
2089        (quote! {crate}, quote!{crate::util::auth::admin::Request})
2090    };
2091    let form_path: syn::Path = syn::parse_str(&format!("super::forms::{}Form", name)).unwrap();
2092    let mut admin_form = quote! {type AdminForm = #form_path;};
2093    let mut add_form = quote! {type AddForm = #form_path;};
2094    let mut fv = vec![];
2095    let mut auto_fields = true;
2096    let search = if let Some(search) = hm.remove("search_fields") {
2097        let all = if let Expr::Array(arr) = search {
2098            let mut v = vec![];
2099            for elem in arr.elems.iter() {
2100                v.push(elem.clone());
2101            }
2102            v
2103        } else {
2104            panic!("expected array");
2105        };
2106        let first = &all[0];
2107        let rest = &all[1..];
2108        let q = quote! {
2109            fn searchable() -> bool {
2110                true
2111            }
2112            fn search(terms: &Vec<String>) -> anansi::db::Whose<Self> {
2113                use anansi::records::Record;
2114                use super::records::#lower::*;
2115                let mut s = if terms.is_empty() {
2116                    return Self::whose(#first().contains(""));
2117                } else {
2118                    Self::whose(#first().icontains(&terms[0]))
2119                };
2120                s = s #(.or(#rest().icontains(&terms[0])))*;
2121                for term in &terms[1..] {
2122                    s = s #(.or(#all().icontains(term)))*
2123                }
2124                s
2125            }
2126        };
2127        q
2128    } else {
2129        quote! {}
2130    };
2131    fv.push(search);
2132    for (name, expr) in &input.hm {
2133        match name.as_str() {
2134            "form" => {
2135                admin_form = quote! {type AdminForm = #expr;};
2136            },
2137            "add_form" => {
2138                add_form = quote! {type AddForm = #expr;};
2139            },
2140            "fields" => {
2141                if let Expr::Array(arr) = &expr {
2142                    auto_fields = false;
2143                    let mut es = vec![];
2144                    let mut elements = vec![];
2145                    let elems = &arr.elems;
2146                    for elem in elems {
2147                        es.push(quote! {#elem}.to_string());
2148                        elements.push(elem);
2149                    }
2150                    fv.push(quote! {
2151                        fn field_names() -> &'static [&'static str] {
2152                            &[#(#es,)*]
2153                        }
2154                        async fn fields(self, _req: &R) -> Vec<String> {
2155                            use anansi::admin_site::AdminField;
2156                            vec![#(self.#elements.admin_field(),)*]
2157                        }
2158                    });
2159                } else {
2160                    panic!("Expected array");
2161                }
2162            },
2163            _ => panic!("Unexpected field: {}", name),
2164        }
2165    }
2166    if auto_fields {
2167        fv.push(quote! {
2168            fn field_names() -> &'static [&'static str] {
2169                use anansi::forms::Form;
2170                Self::AdminForm::field_names()
2171            }
2172            async fn fields(self, req: &R) -> Vec<String> {
2173                use anansi::forms::{Form, GetData};
2174                let data = Self::AdminForm::from_record(self, req).await.unwrap();
2175                let mut form = Self::AdminForm::from_data(data).await;
2176                form.fill().unwrap();
2177                let mut v = vec![];
2178                for field in form.fields() {
2179                    let s = field.widget().attrs().get("value").unwrap().clone();
2180                    v.push(s);
2181                }
2182                v
2183            }
2184        });
2185    }
2186    let q = quote! {
2187        #[async_trait::async_trait]
2188        impl<R: #request> #cra::util::admin::site::RecordAdmin<R> for #path {
2189            #admin_form
2190            #add_form
2191            #(#fv)*
2192        }
2193    };
2194    q.into()
2195}
2196
2197#[proc_macro]
2198pub fn raw_bulk_update(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
2199    let input = parse_macro_input!(input as SchemaArgs);
2200    let vars = input.vars;
2201    let req = &vars[0];
2202    let record_type = &vars[1];
2203    let records = &vars[2];
2204    let field_names = &vars[3..];
2205    let mut fields = vec![];
2206    for field_name in field_names {
2207        let lowfield = quote! {#field_name}.to_string().to_lowercase();
2208        fields.push(quote! {
2209            u.bulk_set(#lowfield, #record_type::PK_NAME);
2210            for record in #records {
2211                u.when(record.pk().to_sql());
2212                u.then(record.#field_name.to_sql());
2213            }
2214            u.end();
2215        });
2216    }
2217    let q = quote! {
2218        {
2219            use anansi::records::ToSql;
2220            let mut u = anansi::db::Update::new(&#record_type::table_name());
2221            #(
2222                #fields
2223            )*
2224            u.where_pk(#record_type::PK_NAME.to_string())
2225                .is_in(#records)
2226                .raw_update(#req.raw().pool())
2227        }
2228    };
2229    q.into()
2230}
2231
2232#[proc_macro]
2233pub fn init_admin(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
2234    let input = parse_macro_input!(input as SchemaArgs);
2235    let vars = input.vars;
2236    let q = quote! {
2237        pub fn initialize_admin<R: anansi::util::auth::admin::Request + crate::project::Request>(site: anansi::util::admin::site::AdminRef<R>) {
2238            let mut site = site.lock().unwrap();
2239            #(#vars)*
2240        }
2241    };
2242    q.into()
2243}
2244
2245#[proc_macro]
2246pub fn register(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
2247    let input = parse_macro_input!(input as syn::Expr);
2248    let ps = quote! {#input}.to_string();
2249    let name = if let Some((_, n)) = ps.rsplit_once("::") {
2250        n
2251    } else {
2252        &ps
2253    };
2254    let lower = name.to_lowercase();
2255    let q = quote! {
2256        {
2257            use super::APP_NAME;
2258            use anansi::admin_site::RecordAdmin;
2259            site.register(anansi::humanize::capitalize(APP_NAME), anansi::admin_site::RecordEntry {name: #name, index: anansi::util::auth::admin::AuthAdminView::record_index::<#input>, new: anansi::util::auth::admin::AuthAdminView::record_new::<#input>});
2260            site.urls_mut().push((concat!("/admin/", #lower), anansi::util::auth::admin::AuthAdminView::record_index::<#input>));
2261            site.urls_mut().push((concat!("/admin/", #lower, "/new"), anansi::util::auth::admin::AuthAdminView::record_new::<#input>));
2262            site.urls_mut().push((concat!("/admin/", #lower, "/edit/{", #lower, "_id}"), anansi::util::auth::admin::AuthAdminView::record_edit::<#input>));
2263            if <#input as RecordAdmin<R>>::searchable() {
2264                site.urls_mut().push((concat!("/admin/", #lower, "/search"), anansi::util::auth::admin::AuthAdminView::record_search::<#input>));
2265                site.urls_mut().push((concat!("/admin/", #lower, "/filter/new"), anansi::util::auth::admin::AuthAdminView::filter_new::<#input>));
2266            }
2267        }
2268    };
2269    q.into()
2270}
2271
2272#[proc_macro]
2273pub fn url(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
2274    let input = parse_macro_input!(input as UrlArgs);
2275    let req = &input.req;
2276    let first = &input.first;
2277    let exprs = &input.exprs;
2278    let q = quote! {
2279        #req.reverse(#first, &[#(&anansi::records::ToUrl::to_url(&#exprs)),*])
2280    };
2281    q.into()
2282}
2283
2284#[proc_macro]
2285pub fn schemas(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
2286    let input = parse_macro_input!(input as SchemaArgs);
2287    let vars = &input.vars;
2288    let q = quote! {
2289        pub const SCHEMAS: &[anansi::syntax::Schema] = &[
2290            #(#vars::schema),*
2291        ];
2292    };
2293    q.into()
2294}
2295
2296#[proc_macro_attribute]
2297pub fn function_component(args: proc_macro::TokenStream, _input: proc_macro::TokenStream) -> proc_macro::TokenStream {
2298    let args = parse_macro_input!(args as Ident);
2299    let id = quote!{#args}.to_string().trim().to_lowercase();
2300    let name = format!(".parsed/{}.rs", id);
2301    quote! {
2302        include!(#name);
2303    }.into()
2304}
2305
2306#[proc_macro_attribute]
2307pub fn component(args: proc_macro::TokenStream, _input: proc_macro::TokenStream) -> proc_macro::TokenStream {
2308    let args = parse_macro_input!(args as Ident);
2309    let id = quote!{#args}.to_string().trim().to_lowercase();
2310    let name = format!(".parsed/{}.rs", id);
2311    quote! {
2312        include!(#name);
2313    }.into()
2314}