tako_rs_streams/static/
file.rs1use std::path::PathBuf;
2
3#[cfg(feature = "compio")]
4use compio::fs;
5use http::StatusCode;
6use tako_rs_core::body::TakoBody;
7use tako_rs_core::responder::Responder;
8use tako_rs_core::types::Request;
9use tako_rs_core::types::Response;
10#[cfg(not(feature = "compio"))]
11use tokio::fs;
12
13#[doc(alias = "serve_file")]
15pub struct ServeFile {
16 path: PathBuf,
17}
18
19#[must_use]
21pub struct ServeFileBuilder {
22 path: PathBuf,
23}
24
25impl ServeFileBuilder {
26 #[inline]
28 pub fn new<P: Into<PathBuf>>(path: P) -> Self {
29 Self { path: path.into() }
30 }
31
32 #[inline]
34 #[must_use]
35 pub fn build(self) -> ServeFile {
36 ServeFile { path: self.path }
37 }
38}
39
40impl ServeFile {
41 pub fn builder<P: Into<PathBuf>>(path: P) -> ServeFileBuilder {
43 ServeFileBuilder::new(path)
44 }
45
46 async fn serve_file(&self) -> Option<Response> {
48 match fs::read(&self.path).await {
49 Ok(contents) => {
50 let mime = mime_guess::from_path(&self.path).first_or_octet_stream();
51 Some(
52 http::Response::builder()
53 .status(StatusCode::OK)
54 .header(http::header::CONTENT_TYPE, mime.to_string())
55 .body(TakoBody::from(contents))
56 .unwrap(),
57 )
58 }
59 Err(_) => None,
60 }
61 }
62
63 pub async fn handle(&self, _req: Request) -> impl Responder {
71 if let Some(resp) = self.serve_file().await {
72 resp
73 } else {
74 let mut resp = http::Response::new(TakoBody::from("File not found"));
75 *resp.status_mut() = StatusCode::NOT_FOUND;
76 resp
77 }
78 }
79}