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 = <.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}