errors.rs

 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}