use std::sync::Arc; use axum::{ extract::{Path, State}, response::{Html, IntoResponse, Redirect}, routing::get, Router, }; use hyper::{header::CONTENT_TYPE, StatusCode}; use serde_derive::Serialize; use tracing::instrument; use crate::{post::Post, AppState, WebsiteError}; pub fn router() -> Router> { Router::new() .route("/tags", get(|| async { Redirect::permanent("/") })) .route("/tags/", get(index)) .route("/tags/:tag", get(redirect)) .route("/tags/:tag/", get(view)) .route("/tags/:tag/atom.xml", get(feed)) } #[derive(Serialize, Debug)] struct TagContext<'a> { title: &'a str, } #[instrument(skip(state))] pub async fn index(State(state): State>) -> Result, WebsiteError> { let tags: Vec<_> = state.tags.values().collect(); let ctx = TagContext { title: "Tags" }; let mut c = tera::Context::new(); c.insert("page", &ctx); c.insert("tags", &tags); let res = state.tera.render("tags_index.html", &c)?; Ok(Html(res)) } #[instrument(skip(state))] pub async fn view( Path(tag): Path, State(state): State>, ) -> Result, WebsiteError> { let mut posts: Vec<&Post> = state .posts .values() .filter(|p| p.is_published() && p.tags.contains(&tag)) .collect(); posts.sort_by_key(|p| &p.date); posts.reverse(); let title = format!("Posts tagged with #{tag}"); let ctx = TagContext { title: &title }; let mut c = tera::Context::new(); c.insert("tag_slug", &tag); c.insert("page", &ctx); c.insert("posts", &posts); let res = state.tera.render("tag.html", &c)?; Ok(Html(res)) } pub async fn feed( Path(slug): Path, State(state): State>, ) -> Result { let tag = state.tags.get(&slug).ok_or(WebsiteError::NotFound)?; let mut posts: Vec<&Post> = state .posts .values() .filter(|p| p.is_published() && p.tags.contains(&slug)) .collect(); posts.sort_by_key(|p| &p.date); posts.reverse(); posts.truncate(10); Ok(( StatusCode::OK, [(CONTENT_TYPE, "application/atom+xml")], crate::feed::render_atom_tag_feed(tag, &state)?, )) } #[instrument(skip(state))] pub async fn redirect( Path(slug): Path, State(state): State>, ) -> Result { if state.tags.contains_key(&slug) { Ok(Redirect::permanent(&format!("/tags/{slug}/"))) } else { Err(WebsiteError::NotFound) } }