titan_derive/
lib.rs

1use proc_macro::TokenStream;
2use proc_macro2::TokenStream as TokenStream2;
3use syn::parse_macro_input;
4use syn::ItemFn;
5
6#[proc_macro_attribute]
7pub fn ssg(_: TokenStream, input: TokenStream) -> TokenStream {
8  let item = parse_macro_input!(input as ItemFn);
9
10  if !item.sig.inputs.is_empty() {
11    return syn::Error::new(
12      item.sig.ident.span(),
13      "Error: SSG routes cannot have arguments",
14    )
15    .into_compile_error()
16    .into();
17  }
18  impl_ssg(item).into()
19}
20
21fn impl_ssg(item: ItemFn) -> TokenStream2 {
22  let struct_ident = item.sig.ident.clone();
23
24  let ident_cache_str =
25    format!("{}_CACHE", item.sig.ident.to_string().to_uppercase());
26  let ident_cache = syn::Ident::new(&ident_cache_str, item.sig.ident.span());
27
28  let nice_fn = item.block;
29
30  quote::quote! {
31    titan::lazy_static! {
32      static ref #ident_cache: std::sync::RwLock<Option<Vec<u8>>> = std::sync::RwLock::new(None);
33    }
34
35    #[allow(non_camel_case_types)]
36    #[derive(Clone)]
37    pub struct #struct_ident;
38
39    impl titan::Handler<()> for #struct_ident {
40      type Output = titan::http::Response;
41      type Future =
42        std::pin::Pin<Box<dyn std::future::Future<Output = Self::Output> + Send>>;
43      fn call(&self, _: ()) -> Self::Future {
44        let _lock = match #ident_cache.read() {
45          Ok(v) => v,
46          Err(_) => panic!("oh no"),
47        };
48
49        if let Some(cache) = _lock.as_ref() {
50          let body =
51            titan::http::body::Body::Full(cache.clone().into_boxed_slice());
52          return Box::pin(async move {
53            titan::http::ResponseBuilder::new().status(200).body(body).unwrap()
54          });
55        };
56
57        Box::pin(async move {
58          let response = titan::FutureExt::map(async #nice_fn, |x| x.respond()).await;
59
60
61          match response.body() {
62            titan::http::body::Body::Full(ref body) => {
63              let mut refs = #ident_cache.write().unwrap();
64              *refs = Some(body.clone().to_vec());
65            }
66            titan::http::body::Body::Stream(_) => {
67              panic!(
68                "Body::Stream is not available in a cached request response :("
69              )
70            }
71          };
72          response
73        })
74      }
75    }
76  }
77}