simple_api/
views.rs

1use anyhow::anyhow;
2use async_trait::async_trait;
3use hyper::{header, Body, Method, Response, StatusCode};
4use regex::Regex;
5use tokio::fs::File;
6use tokio::io::AsyncReadExt; // for read_to_end()
7
8use crate::{
9    context::Context,
10    types::{HttpRequest, HttpResonse},
11    view::View,
12};
13
14pub struct StaticFiles {
15    root: String,
16    re_path: Regex,
17}
18
19impl StaticFiles {
20    pub fn new(root: String, re_path: Regex) -> Self {
21        Self { root, re_path }
22    }
23}
24
25#[async_trait]
26impl View for StaticFiles {
27    fn re_path(&self) -> Regex {
28        self.re_path.clone()
29    }
30    fn methods(&self) -> Vec<Method> {
31        vec![Method::GET]
32    }
33    async fn call(&self, req: &mut HttpRequest, ctx: &mut Context) -> anyhow::Result<HttpResonse> {
34        let view_args = &ctx.view_args;
35        let file_path = view_args
36            .as_ref()
37            .ok_or(anyhow!("no view_args"))?
38            .get("file_path")
39            .ok_or(anyhow!("no file_path"))?;
40        let file_path = format!("{}/{}", self.root, file_path);
41        let file_path = std::path::Path::new(&file_path);
42        if !file_path.exists() {
43            return Ok(Response::builder()
44                .status(StatusCode::NOT_FOUND)
45                .body(Body::from("Not found"))?);
46        }
47
48        let mut file = tokio::fs::File::open(file_path).await?;
49        let mut buf: Vec<u8> = Vec::new();
50        file.read_to_end(&mut buf).await?;
51        let mime = mime_guess::from_path(file_path).first_or_octet_stream();
52        let mut r = Response::builder()
53            .status(StatusCode::OK)
54            .body(Body::from(buf))?;
55        r.headers_mut()
56            .insert(header::CONTENT_TYPE, (&mime.to_string()).parse()?);
57        Ok(r)
58    }
59}