From 611baba0ce2ba273c7277f39cc7f3c8db0c72a98 Mon Sep 17 00:00:00 2001 From: Christine Dodrill Date: Mon, 13 Jul 2020 07:54:17 -0400 Subject: [PATCH] render blogposts --- src/handlers/mod.rs | 95 ++++++++++++++++++++++++++++- src/main.rs | 63 ++++++++++++------- templates/blogpost.rs.html | 120 +++++++++++++++++++++++++++++++++++++ 3 files changed, 252 insertions(+), 26 deletions(-) create mode 100644 templates/blogpost.rs.html diff --git a/src/handlers/mod.rs b/src/handlers/mod.rs index b30cd07..913a045 100644 --- a/src/handlers/mod.rs +++ b/src/handlers/mod.rs @@ -3,8 +3,11 @@ use crate::{ post::Post, templates::{self, Html, RenderRucte}, }; -use std::sync::Arc; -use warp::{http::Response, Rejection, Reply}; +use std::{convert::Infallible, fmt, sync::Arc}; +use warp::{ + http::{Response, StatusCode}, + Rejection, Reply, +}; pub async fn index() -> Result { Response::builder().html(|o| templates::index_html(o)) @@ -67,5 +70,91 @@ pub async fn blog_series_view(series: String, state: Arc) -> Result) -> Result { + let mut want: Option = None; + + for post in &state.blog { + log::debug!("{}", post.link); + if post.link == format!("blog/{}", name) { + want = Some(post.clone()); + } + } + + match want { + None => Err(PostNotFound("blog".into(), name).into()), + Some(post) => { + let body = Html(post.body_html.clone()); + Response::builder().html(|o| templates::blogpost_html(o, post, body)) + } + } +} + +#[derive(Debug, thiserror::Error)] +struct PostNotFound(String, String); + +impl fmt::Display for PostNotFound { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "not found: {}/{}", self.0, self.1) + } +} + +impl warp::reject::Reject for PostNotFound {} + +impl From for warp::reject::Rejection { + fn from(error: PostNotFound) -> Self { + warp::reject::custom(error) + } +} + +#[derive(Debug, thiserror::Error)] +struct SeriesNotFound(String); + +impl fmt::Display for SeriesNotFound { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.0) + } +} + +impl warp::reject::Reject for SeriesNotFound {} + +impl From for warp::reject::Rejection { + fn from(error: SeriesNotFound) -> Self { + warp::reject::custom(error) + } +} + +pub async fn rejection(err: Rejection) -> Result { + let path: String; + let code; + + if err.is_not_found() { + path = "".into(); + code = StatusCode::NOT_FOUND; + } else if let Some(SeriesNotFound(series)) = err.find() { + log::error!("invalid series {}", series); + path = format!("/blog/series/{}", series); + code = StatusCode::NOT_FOUND; + } else if let Some(PostNotFound(kind, name)) = err.find() { + log::error!("unknown post {}/{}", kind, name); + path = format!("/{}/{}", kind, name); + code = StatusCode::NOT_FOUND; + } else { + log::error!("unhandled rejection: {:?}", err); + path = "wut".into(); + code = StatusCode::INTERNAL_SERVER_ERROR; + } + + Ok(warp::reply::with_status( + Response::builder() + .html(|o| templates::notfound_html(o, path)) + .unwrap(), + code, + )) } diff --git a/src/main.rs b/src/main.rs index 61647c1..b24c2c6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -39,35 +39,51 @@ async fn main() -> Result<()> { let series = base.and( warp::path!("series").and(with_state(state.clone()).and_then(handlers::blog_series)), ); + let series_view = base.and( + warp::path!("series" / String) + .and(with_state(state.clone())) + .and(warp::get()) + .and_then(handlers::blog_series_view), + ); + let post_view = base.and( + warp::path!(String) + .and(with_state(state.clone())) + .and(warp::get()) + .and_then(handlers::blog_post_view), + ); - index.or(series) + index.or(series.or(series_view)).or(post_view) }; - // let blog_series_view = warp::path!("blog" / "series" / String) - // .and(warp::get()) - // .and(with_state(state.clone()) - // .and_then( move | series: String, state: Arc | handlers::blog_series_view(series, state))); + let static_pages = { + let contact = warp::path!("contact").and_then(handlers::contact); + let feeds = warp::path!("feeds").and_then(handlers::feeds); + let resume = warp::path!("resume") + .and(with_state(state.clone())) + .and_then(handlers::resume); + let signalboost = warp::path!("signalboost") + .and(with_state(state.clone())) + .and_then(handlers::signalboost); + + contact.or(feeds.or(resume.or(signalboost))) + }; let routes = warp::get() .and(path::end().and_then(handlers::index)) - .or(warp::path!("contact").and_then(handlers::contact)) - .or(warp::path!("feeds").and_then(handlers::feeds)) - .or(warp::path!("resume") - .and(with_state(state.clone())) - .and_then(handlers::resume)) - .or(warp::path!("signalboost") - .and(with_state(state.clone())) - .and_then(handlers::signalboost)) - .or(blog) - .or(warp::any().and_then(handlers::not_found)); - let files = warp::path("static") - .and(warp::fs::dir("./static")) - .or(warp::path("css").and(warp::fs::dir("./css"))) - .or(warp::path("sw.js").and(warp::fs::file("./static/js/sw.js"))) - .or(warp::path("robots.txt").and(warp::fs::file("./static/robots.txt"))); + .or(static_pages) + .or(blog); - let site = routes - .or(files) + let files = { + let files = warp::path("static").and(warp::fs::dir("./static")); + let css = warp::path("css").and(warp::fs::dir("./css")); + let sw = warp::path("sw.js").and(warp::fs::file("./static/js/sw.js")); + let robots = warp::path("robots.txt").and(warp::fs::file("./static/robots.txt")); + + files.or(css).or(sw).or(robots) + }; + + let site = files + .or(routes) .map(|reply| { warp::reply::with_header( reply, @@ -76,7 +92,8 @@ async fn main() -> Result<()> { ) }) .or(healthcheck) - .with(warp::log(APPLICATION_NAME)); + .with(warp::log(APPLICATION_NAME)) + .recover(handlers::rejection); warp::serve(site).run(([127, 0, 0, 1], 3030)).await; diff --git a/templates/blogpost.rs.html b/templates/blogpost.rs.html new file mode 100644 index 0000000..2831b67 --- /dev/null +++ b/templates/blogpost.rs.html @@ -0,0 +1,120 @@ +@use super::{header_html, footer_html}; +@use crate::post::Post; + +@(post: Post, body: impl ToHtml) + +@:header_html(Some(&post.front_matter.title.clone()), None) + + + + + + + + + + + + + + + + + + + + +@body + +
+ + + + +@if post.front_matter.series.is_some() { +

Series: @post.front_matter.series.as_ref().unwrap()

+} + +@if post.front_matter.tags.is_some() { +

Tags: @for tag in post.front_matter.tags.as_ref().unwrap() { @tag }

+} + + + +@:footer_html()