1use crate::{AppState, LayoutData, Request, RequestExt};
2use async_trait::async_trait;
3use serde::Serialize;
4use std::sync::Arc;
5use tide::http::mime;
6
7pub struct Middleware;
8
9#[async_trait]
10impl tide::Middleware<Arc<AppState>> for Middleware {
11 async fn handle(
12 &self,
13 mut request: Request,
14 next: tide::Next<'_, Arc<AppState>>,
15 ) -> tide::Result {
16 let app = request.state().clone();
17 let layout_data = request.layout_data().await?;
18
19 let mut response = next.run(request).await;
20
21 #[derive(Serialize)]
22 struct ErrorData {
23 #[serde(flatten)]
24 layout: Arc<LayoutData>,
25 status: u16,
26 reason: &'static str,
27 }
28
29 if !response.status().is_success() {
30 response.set_body(app.render_template(
31 "error.hbs",
32 &ErrorData {
33 layout: layout_data,
34 status: response.status().into(),
35 reason: response.status().canonical_reason(),
36 },
37 )?);
38 response.set_content_type(mime::HTML);
39 }
40
41 Ok(response)
42 }
43}
44
45// Allow tide Results to accept context like other Results do when
46// using anyhow.
47pub trait TideResultExt {
48 fn context<C>(self, cx: C) -> Self
49 where
50 C: std::fmt::Display + Send + Sync + 'static;
51
52 fn with_context<C, F>(self, f: F) -> Self
53 where
54 C: std::fmt::Display + Send + Sync + 'static,
55 F: FnOnce() -> C;
56}
57
58impl<T> TideResultExt for tide::Result<T> {
59 fn context<C>(self, cx: C) -> Self
60 where
61 C: std::fmt::Display + Send + Sync + 'static,
62 {
63 self.map_err(|e| tide::Error::new(e.status(), e.into_inner().context(cx)))
64 }
65
66 fn with_context<C, F>(self, f: F) -> Self
67 where
68 C: std::fmt::Display + Send + Sync + 'static,
69 F: FnOnce() -> C,
70 {
71 self.map_err(|e| tide::Error::new(e.status(), e.into_inner().context(f())))
72 }
73}