From a84e3772a58ab19f8807beb32616547f1cdc9742 Mon Sep 17 00:00:00 2001
From: Vincent Thomas <77443389+vincent-thomas@users.noreply.github.com>
Date: Thu, 16 Jan 2025 17:59:13 +0100
Subject: [PATCH] ssg
---
Cargo.lock | 11 +++++
Cargo.toml | 2 +-
titan-core/src/respond.rs | 16 ++++----
titan-derive/Cargo.toml | 19 +++++++++
titan-derive/src/lib.rs | 77 +++++++++++++++++++++++++++++++++++
titan-html-derive/src/lib.rs | 34 ++++++++++++++--
titan-html/src/lib.rs | 4 +-
titan/Cargo.toml | 2 +
titan/examples/hello_world.rs | 15 ++++++-
titan/examples/nice_fns.rs | 10 +++++
titan/src/lib.rs | 4 ++
titan/src/utils.rs | 5 +++
12 files changed, 181 insertions(+), 18 deletions(-)
create mode 100644 titan-derive/Cargo.toml
create mode 100644 titan-derive/src/lib.rs
diff --git a/Cargo.lock b/Cargo.lock
index 6285cc1..382cfbc 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1570,11 +1570,13 @@ version = "0.4.0"
dependencies = [
"futures-util",
"lambda_http",
+ "lazy_static",
"pin-project-lite",
"serde",
"serde_json",
"serde_urlencoded",
"titan-core",
+ "titan-derive",
"titan-html",
"titan-http",
"titan-router",
@@ -1594,6 +1596,15 @@ dependencies = [
"tower 0.5.2",
]
+[[package]]
+name = "titan-derive"
+version = "0.4.0"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.95",
+]
+
[[package]]
name = "titan-html"
version = "0.4.0"
diff --git a/Cargo.toml b/Cargo.toml
index a0a22ac..9d16096 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -11,7 +11,7 @@ members = [
"titan-html-core",
"titan-html-derive",
"titan-lambda"
-]
+, "titan-derive"]
[workspace.package]
authors = ["Vincent Thomas"]
diff --git a/titan-core/src/respond.rs b/titan-core/src/respond.rs
index 1fa2de7..de5499d 100644
--- a/titan-core/src/respond.rs
+++ b/titan-core/src/respond.rs
@@ -42,7 +42,7 @@ use titan_http::{body::Body, header, Response, ResponseBuilder, StatusCode};
/// // converted into an HTTP response.
/// ```
pub trait Respondable {
- fn respond(self) -> Response
;
+ fn respond(self) -> Response;
}
impl Respondable for Result
@@ -50,7 +50,7 @@ where
T: Respondable,
E: Respondable,
{
- fn respond(self) -> Response {
+ fn respond(self) -> Response {
match self {
Ok(t) => t.respond(),
Err(e) => e.respond(),
@@ -58,20 +58,20 @@ where
}
}
-impl Respondable for Response {
- fn respond(self) -> Response {
+impl Respondable for Response {
+ fn respond(self) -> Response {
self
}
}
impl Respondable for Infallible {
- fn respond(self) -> Response {
+ fn respond(self) -> Response {
panic!("Not fallible :(")
}
}
impl Respondable for () {
- fn respond(self) -> Response {
+ fn respond(self) -> Response {
ResponseBuilder::new().status(204).body(Body::from(())).unwrap()
}
}
@@ -80,7 +80,7 @@ impl Respondable for (StatusCode, T)
where
T: Respondable,
{
- fn respond(self) -> Response {
+ fn respond(self) -> Response {
let (status, body) = self;
let mut res = body.respond();
@@ -111,7 +111,7 @@ macro_rules! impl_respondable_for_int {
($($t:ty)*) => {
$(
impl Respondable for $t {
- fn respond(self) -> Response {
+ fn respond(self) -> Response {
let body = Body::from(self);
let mut res = Response::new(body);
diff --git a/titan-derive/Cargo.toml b/titan-derive/Cargo.toml
new file mode 100644
index 0000000..793ed1a
--- /dev/null
+++ b/titan-derive/Cargo.toml
@@ -0,0 +1,19 @@
+[package]
+name = "titan-derive"
+authors.workspace = true
+categories.workspace = true
+keywords.workspace = true
+license.workspace = true
+edition.workspace = true
+version.workspace = true
+repository.workspace = true
+documentation.workspace = true
+rust-version.workspace = true
+
+[lib]
+proc-macro = true
+
+[dependencies]
+syn = { version = "2", features = ["full"] }
+quote = "1"
+proc-macro2 = "1.0.92"
diff --git a/titan-derive/src/lib.rs b/titan-derive/src/lib.rs
new file mode 100644
index 0000000..6ed8002
--- /dev/null
+++ b/titan-derive/src/lib.rs
@@ -0,0 +1,77 @@
+use proc_macro::TokenStream;
+use proc_macro2::TokenStream as TokenStream2;
+use syn::parse_macro_input;
+use syn::ItemFn;
+
+#[proc_macro_attribute]
+pub fn ssg(_: TokenStream, input: TokenStream) -> TokenStream {
+ let item = parse_macro_input!(input as ItemFn);
+
+ if !item.sig.inputs.is_empty() {
+ return syn::Error::new(
+ item.sig.ident.span(),
+ "Error: SSG routes cannot have arguments",
+ )
+ .into_compile_error()
+ .into();
+ }
+ impl_ssg(item).into()
+}
+
+fn impl_ssg(item: ItemFn) -> TokenStream2 {
+ let struct_ident = item.sig.ident.clone();
+
+ let ident_cache_str =
+ format!("{}_CACHE", item.sig.ident.to_string().to_uppercase());
+ let ident_cache = syn::Ident::new(&ident_cache_str, item.sig.ident.span());
+
+ let nice_fn = item.block;
+
+ quote::quote! {
+ titan::lazy_static! {
+ static ref #ident_cache: std::sync::RwLock