use std::sync::Arc; use axum::{extract::Path, response::Html, Extension}; use cached::proc_macro::cached; use pulldown_cmark::{html, Options, Parser}; use tracing::log::*; use super::{Error, Result}; use crate::{handlers::PageContext, State}; pub async fn view(Path(path): Path, Extension(state): Extension>) -> Result { let post = path.trim_end_matches('/'); info!("Requested post: {}", post); let res = render_post(&state, post).await.ok_or(Error::NotFound)?; Ok(Html(res.into())) } #[cached(time = 60, key = "String", convert = r"{ path.to_owned() }")] async fn render_post(state: &State, path: &str) -> Option { info!("Rendering post..."); let post = state.posts.iter().find(|p| p.slug == path)?; let options = Options::all(); let parser = Parser::new_ext(&post.content, options); let mut out = String::new(); html::push_html(&mut out, parser); let ctx = tera::Context::from_serialize(PageContext { content: out }).ok()?; let res = match state.tera.render("post.html", &ctx) { Ok(res) => res, Err(e) => { error!("Failed rendering post: {}", e); return None; } }; Some(res) } pub async fn index(Extension(state): Extension>) -> Result { let mut ctx = tera::Context::new(); ctx.insert("posts", &state.posts); let res = state.tera.render("postsindex.html", &ctx).map_err(|e| { error!("Failed rendering posts index: {:?}", e); Error::NotFound })?; Ok(Html(res.into())) }