tower_http/services/fs/serve_dir/
backend.rs1use std::{future::Future, io, path::PathBuf, pin::Pin, time::SystemTime};
7use tokio::io::{AsyncRead, AsyncSeek};
8
9pub trait Metadata: Send + 'static {
13 fn is_dir(&self) -> bool;
15
16 fn modified(&self) -> io::Result<SystemTime>;
18
19 fn len(&self) -> u64;
21
22 fn is_empty(&self) -> bool {
24 self.len() == 0
25 }
26}
27
28pub trait File: AsyncRead + AsyncSeek + Unpin + Send + Sync {
33 type Metadata: Metadata;
35
36 type MetadataFuture<'a>: Future<Output = io::Result<Self::Metadata>> + Send
38 where
39 Self: 'a;
40
41 fn metadata(&self) -> Self::MetadataFuture<'_>;
43}
44
45pub trait Backend: Clone + Send + Sync + 'static {
50 type File: File<Metadata = Self::Metadata>;
52
53 type Metadata: Metadata;
55
56 type OpenFuture: Future<Output = io::Result<Self::File>> + Send;
58
59 type MetadataFuture: Future<Output = io::Result<Self::Metadata>> + Send;
61
62 fn open(&self, path: PathBuf) -> Self::OpenFuture;
64
65 fn metadata(&self, path: PathBuf) -> Self::MetadataFuture;
67}
68
69#[derive(Clone, Debug, Default)]
71pub struct TokioBackend;
72
73impl Backend for TokioBackend {
74 type File = TokioFile;
75 type Metadata = std::fs::Metadata;
76 type OpenFuture = Pin<Box<dyn Future<Output = io::Result<TokioFile>> + Send>>;
77 type MetadataFuture = Pin<Box<dyn Future<Output = io::Result<std::fs::Metadata>> + Send>>;
78
79 fn open(&self, path: PathBuf) -> Self::OpenFuture {
80 Box::pin(async move {
81 let file = tokio::fs::File::open(&path).await?;
82 Ok(TokioFile(file))
83 })
84 }
85
86 fn metadata(&self, path: PathBuf) -> Self::MetadataFuture {
87 Box::pin(async move { tokio::fs::metadata(&path).await })
88 }
89}
90
91#[derive(Debug)]
93pub struct TokioFile(tokio::fs::File);
94
95impl AsyncRead for TokioFile {
96 fn poll_read(
97 mut self: Pin<&mut Self>,
98 cx: &mut std::task::Context<'_>,
99 buf: &mut tokio::io::ReadBuf<'_>,
100 ) -> std::task::Poll<io::Result<()>> {
101 Pin::new(&mut self.0).poll_read(cx, buf)
102 }
103}
104
105impl AsyncSeek for TokioFile {
106 fn start_seek(mut self: Pin<&mut Self>, position: io::SeekFrom) -> io::Result<()> {
107 Pin::new(&mut self.0).start_seek(position)
108 }
109
110 fn poll_complete(
111 mut self: Pin<&mut Self>,
112 cx: &mut std::task::Context<'_>,
113 ) -> std::task::Poll<io::Result<u64>> {
114 Pin::new(&mut self.0).poll_complete(cx)
115 }
116}
117
118impl File for TokioFile {
119 type Metadata = std::fs::Metadata;
120 type MetadataFuture<'a> =
121 Pin<Box<dyn Future<Output = io::Result<std::fs::Metadata>> + Send + 'a>>;
122
123 fn metadata(&self) -> Self::MetadataFuture<'_> {
124 Box::pin(async move { self.0.metadata().await })
125 }
126}
127
128impl Metadata for std::fs::Metadata {
129 fn is_dir(&self) -> bool {
130 self.is_dir()
131 }
132
133 fn modified(&self) -> io::Result<SystemTime> {
134 self.modified()
135 }
136
137 fn len(&self) -> u64 {
138 self.len()
139 }
140}