use std::net::Ipv4Addr;
use axum::{
extract::{rejection::MatchedPathRejection, FromRef, FromRequestParts, MatchedPath, Path},
http::request::Parts,
response::IntoResponse,
routing::get,
serve, RequestPartsExt, Router,
};
use axum_template::{engine::Engine, RenderHtml};
use serde::Serialize;
use tera::Tera;
use tokio::net::TcpListener;
pub struct CustomKey(pub String);
impl<S> FromRequestParts<S> for CustomKey
where
S: Send + Sync,
{
type Rejection = MatchedPathRejection;
async fn from_request_parts(parts: &mut Parts, _: &S) -> Result<Self, Self::Rejection> {
let key = parts
.extract::<MatchedPath>()
.await?
.as_str()
.chars()
.skip(1)
.chain(".html".chars())
.collect();
Ok(CustomKey(key))
}
}
type AppEngine = Engine<Tera>;
#[derive(Debug, Serialize)]
pub struct Person {
name: String,
}
async fn get_name(
engine: AppEngine,
CustomKey(template): CustomKey,
Path(name): Path<String>,
) -> impl IntoResponse {
let person = Person { name };
RenderHtml(template, engine, person)
}
#[derive(Clone, FromRef)]
struct AppState {
engine: AppEngine,
}
#[tokio::main]
async fn main() {
let tera = Tera::new("examples/templates/tera/**/*.html").expect("Template folder not found");
let app = Router::new()
.route("/{name}", get(get_name))
.with_state(AppState {
engine: Engine::from(tera),
});
println!("See example: http://127.0.0.1:8080/example");
let listener = TcpListener::bind((Ipv4Addr::LOCALHOST, 8080))
.await
.unwrap();
serve(listener, app.into_make_service()).await.unwrap();
}