From ad27e7f06db971eac3a8e88f4e70b3e6090826db Mon Sep 17 00:00:00 2001 From: spencer Date: Sun, 26 Apr 2026 14:38:14 +0000 Subject: [PATCH 1/2] Update title in default template page --- templates/_index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/_index.html b/templates/_index.html index 0252572..05be070 100644 --- a/templates/_index.html +++ b/templates/_index.html @@ -1,5 +1,5 @@ {% import "common.html" as common %} -{{ common::meta(title="Spencer Whitehead - Publications") }} +{{ common::meta(title="") }} {{ common::nav() }} From 0e503a952e123bcf43d59638986f8f53445f0267 Mon Sep 17 00:00:00 2001 From: spencer Date: Sun, 26 Apr 2026 15:45:52 -0400 Subject: [PATCH 2/2] switch warp for axum --- Cargo.lock | 272 ++++++++++++++++++++---------------------- Cargo.toml | 3 +- src/main.rs | 87 +++++++------- templates/common.html | 4 +- 4 files changed, 176 insertions(+), 190 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 64876de..d86062f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -33,10 +33,56 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" [[package]] -name = "base64" -version = "0.22.1" +name = "axum" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" +checksum = "31b698c5f9a010f6573133b09e0de5408834d0c82f8d7475a89fc1867a71cd90" +dependencies = [ + "axum-core", + "bytes", + "form_urlencoded", + "futures-util", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-util", + "itoa", + "matchit", + "memchr", + "mime", + "percent-encoding", + "pin-project-lite", + "serde_core", + "serde_json", + "serde_path_to_error", + "serde_urlencoded", + "sync_wrapper", + "tokio", + "tower", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "axum-core" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08c78f31d7b1291f7ee735c1c6780ccde7785daae9a9206026862dab7d8792d1" +dependencies = [ + "bytes", + "futures-core", + "http", + "http-body", + "http-body-util", + "mime", + "pin-project-lite", + "sync_wrapper", + "tower-layer", + "tower-service", + "tracing", +] [[package]] name = "bitflags" @@ -190,12 +236,6 @@ dependencies = [ "crypto-common", ] -[[package]] -name = "equivalent" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" - [[package]] name = "errno" version = "0.3.14" @@ -212,12 +252,6 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" -[[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - [[package]] name = "form_urlencoded" version = "1.2.2" @@ -261,9 +295,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" dependencies = [ "futures-core", - "futures-sink", "futures-task", "pin-project-lite", + "slab", ] [[package]] @@ -311,55 +345,6 @@ dependencies = [ "walkdir", ] -[[package]] -name = "h2" -version = "0.4.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f44da3a8150a6703ed5d34e164b875fd14c2cdab9af1252a9a1020bde2bdc54" -dependencies = [ - "atomic-waker", - "bytes", - "fnv", - "futures-core", - "futures-sink", - "http", - "indexmap", - "slab", - "tokio", - "tokio-util", - "tracing", -] - -[[package]] -name = "hashbrown" -version = "0.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f467dd6dccf739c208452f8014c75c18bb8301b050ad1cfb27153803edb0f51" - -[[package]] -name = "headers" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3314d5adb5d94bcdf56771f2e50dbbc80bb4bdf88967526706205ac9eff24eb" -dependencies = [ - "base64", - "bytes", - "headers-core", - "http", - "httpdate", - "mime", - "sha1", -] - -[[package]] -name = "headers-core" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54b4a22553d4242c49fddb9ba998a99962b5cc6f22cb5a3482bec22522403ce4" -dependencies = [ - "http", -] - [[package]] name = "http" version = "1.4.0" @@ -393,6 +378,12 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "http-range-header" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9171a2ea8a68358193d15dd5d70c1c10a2afc3e7e4c5bc92bc9f025cebd7359c" + [[package]] name = "httparse" version = "1.10.1" @@ -424,7 +415,6 @@ dependencies = [ "bytes", "futures-channel", "futures-core", - "h2", "http", "http-body", "httparse", @@ -490,16 +480,6 @@ dependencies = [ "winapi-util", ] -[[package]] -name = "indexmap" -version = "2.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" -dependencies = [ - "equivalent", - "hashbrown", -] - [[package]] name = "itoa" version = "1.0.18" @@ -549,6 +529,12 @@ version = "0.4.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" +[[package]] +name = "matchit" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3" + [[package]] name = "memchr" version = "2.8.0" @@ -716,26 +702,6 @@ dependencies = [ "siphasher", ] -[[package]] -name = "pin-project" -version = "1.1.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1749c7ed4bcaf4c3d0a3efc28538844fb29bcdd7d2b67b2be7e20ba861ff517" -dependencies = [ - "pin-project-internal", -] - -[[package]] -name = "pin-project-internal" -version = "1.1.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b20ed30f105399776b9c883e68e536ef602a16ae6f596d2c473591d6ad64c6" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "pin-project-lite" version = "0.2.17" @@ -858,12 +824,6 @@ dependencies = [ "winapi-util", ] -[[package]] -name = "scoped-tls" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" - [[package]] name = "scopeguard" version = "1.2.0" @@ -912,6 +872,17 @@ dependencies = [ "zmij", ] +[[package]] +name = "serde_path_to_error" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10a9ff822e371bb5403e391ecd83e182e0e77ba7f6fe0160b795797109d1b457" +dependencies = [ + "itoa", + "serde", + "serde_core", +] + [[package]] name = "serde_urlencoded" version = "0.7.1" @@ -924,17 +895,6 @@ dependencies = [ "serde", ] -[[package]] -name = "sha1" -version = "0.10.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest", -] - [[package]] name = "sha2" version = "0.10.9" @@ -1011,6 +971,12 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "sync_wrapper" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" + [[package]] name = "tera" version = "1.20.1" @@ -1074,6 +1040,54 @@ dependencies = [ "tokio", ] +[[package]] +name = "tower" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4" +dependencies = [ + "futures-core", + "futures-util", + "pin-project-lite", + "sync_wrapper", + "tokio", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-http" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4e6559d53cc268e5031cd8429d05415bc4cb4aefc4aa5d6cc35fbf5b924a1f8" +dependencies = [ + "bitflags", + "bytes", + "futures-core", + "futures-util", + "http", + "http-body", + "http-body-util", + "http-range-header", + "httpdate", + "mime", + "mime_guess", + "percent-encoding", + "pin-project-lite", + "tokio", + "tokio-util", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-layer" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" + [[package]] name = "tower-service" version = "0.3.3" @@ -1146,35 +1160,6 @@ dependencies = [ "winapi-util", ] -[[package]] -name = "warp" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51d06d9202adc1f15d709c4f4a2069be5428aa912cc025d6f268ac441ab066b0" -dependencies = [ - "bytes", - "futures-util", - "headers", - "http", - "http-body", - "http-body-util", - "hyper", - "hyper-util", - "log", - "mime", - "mime_guess", - "percent-encoding", - "pin-project", - "scoped-tls", - "serde", - "serde_json", - "serde_urlencoded", - "tokio", - "tokio-util", - "tower-service", - "tracing", -] - [[package]] name = "wasi" version = "0.11.1+wasi-snapshot-preview1" @@ -1230,10 +1215,11 @@ dependencies = [ name = "website" version = "0.1.0" dependencies = [ + "axum", "lazy_static", "tera", "tokio", - "warp", + "tower-http", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 0d8045d..440cb86 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,8 @@ version = "0.1.0" edition = "2021" [dependencies] +axum = "0.8.9" lazy_static = "1.5.0" tera = "1.20.1" tokio = { version = "1", features = ["full"] } -warp = { version = "0.4", features = ["server"] } +tower-http = { version = "0.6.8", features = ["fs"] } diff --git a/src/main.rs b/src/main.rs index 22a3dd8..e037ba1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,19 @@ +use std::collections::HashMap; use tera::{Context, Tera}; -use warp::{Filter, filters::path::Tail, Reply, Rejection}; -use warp::hyper::StatusCode; +use axum::{ + Router, + response::{ + Html, + Redirect + }, + extract::Path, + routing::get, + http::{ + header::{HeaderMap, ACCEPT_LANGUAGE}, + StatusCode + } +}; +use tower_http::services::{ServeDir, ServeFile}; lazy_static::lazy_static! { pub static ref TEMPLATES: Tera = Tera::new("templates/**/*").expect("failed to parse templates"); @@ -8,51 +21,37 @@ lazy_static::lazy_static! { #[tokio::main] async fn main() { - let static_pages = warp::fs::dir("static"); + let serve_dir = ServeDir::new("static").not_found_service(ServeFile::new("static/404.html")); - for name in TEMPLATES.get_template_names() { - println!("Loaded template {}", name); - } + let app = Router::new() + .route("/", get(language_redirect)) + .route("/{lang}/", get(render_template)) + .route("/{lang}/{*tail}", get(render_template)) + .fallback_service(serve_dir); - let templates = warp::get() - .and(warp::path::param::()) - .and(warp::path::tail()) - .and_then(render_template); - - let root = warp::path::end() - .and(warp::header::optional::("accept-language")) - .map(|lang: Option| { - let target = match lang { - Some(l) if l.starts_with("fr") => "/fr/", - _ => "/en/", - }; - warp::redirect(warp::http::Uri::from_static(target)) - }); - - let routes = root.or(static_pages) - .or(templates) - .recover(handle_rejection); - - warp::serve(routes) - .run(([0,0,0,0], 3030)) - .await; -} -async fn handle_rejection(err: Rejection) -> Result { - let code; - let message; - if err.is_not_found() { - code = StatusCode::NOT_FOUND; - message = std::fs::read_to_string("static/404.html").expect("/static/404.html must exist for 404 redirects"); - } else { - code = StatusCode::INTERNAL_SERVER_ERROR; - message = String::from("Internal server error"); - } - Ok(warp::reply::with_status(warp::reply::html(message), code)) + let listener = tokio::net::TcpListener::bind("0.0.0.0:3030").await.unwrap(); + axum::serve(listener, app).await.unwrap(); } +async fn language_redirect(headers: HeaderMap) -> Redirect { + let lang = headers + .get(ACCEPT_LANGUAGE) + .and_then(|value| value.to_str().ok()) + .map(|s| { + if s.to_lowercase().starts_with("fr") { + "fr" + } else { + "en" + } + }).unwrap_or("en"); + Redirect::to(&format!("/{}/", lang)) +} -async fn render_template(lang: String, tail: Tail) -> Result { - let mut path = format!("{lang}/{}", tail.as_str()); +async fn render_template(Path(params): Path>) -> Result, StatusCode> { + let lang = params.get("lang").ok_or(StatusCode::BAD_REQUEST)?; + let tail = params.get("tail").map(|s| s.as_str()).unwrap_or("index.html"); + + let mut path = format!("{lang}/{tail}"); if path.is_empty() || path.ends_with('/') { path.push_str("index.html"); } @@ -61,10 +60,10 @@ async fn render_template(lang: String, tail: Tail) -> Result Ok(warp::reply::html(html)), + Ok(html) => Ok(Html(html)), Err(e) => { eprintln!("Tera error: {}", e); - Err(warp::reject::not_found()) + Err(StatusCode::NOT_FOUND) } } } diff --git a/templates/common.html b/templates/common.html index 2847099..02fe8dc 100644 --- a/templates/common.html +++ b/templates/common.html @@ -13,9 +13,9 @@ {% macro nav() %} {% endmacro %}