1use futures::io::{AsyncRead, AsyncSeek, AsyncWrite};
7use std::io;
8#[cfg(feature = "tokio-providers")]
9use std::io::SeekFrom;
10#[cfg(feature = "tokio-providers")]
11use std::pin::Pin;
12#[cfg(feature = "tokio-providers")]
13use std::task::{Context, Poll};
14#[cfg(feature = "tokio-providers")]
15use tokio_util::compat::{Compat, TokioAsyncReadCompatExt};
16
17#[derive(Debug, Clone, Default)]
23pub struct OpenOptions {
24 flags: u8,
26}
27
28impl OpenOptions {
29 const FLAG_READ: u8 = 1 << 0;
30 const FLAG_WRITE: u8 = 1 << 1;
31 const FLAG_CREATE: u8 = 1 << 2;
32 const FLAG_CREATE_NEW: u8 = 1 << 3;
33 const FLAG_TRUNCATE: u8 = 1 << 4;
34 const FLAG_APPEND: u8 = 1 << 5;
35
36 #[must_use]
38 pub fn new() -> Self {
39 Self::default()
40 }
41
42 fn set_flag(mut self, flag: u8, value: bool) -> Self {
43 if value {
44 self.flags |= flag;
45 } else {
46 self.flags &= !flag;
47 }
48 self
49 }
50}
51
52macro_rules! flag_setters {
53 ($($(#[$attr:meta])* $name:ident => $flag:ident),* $(,)?) => {
54 impl OpenOptions {
55 $(
56 $(#[$attr])*
57 #[must_use]
58 pub fn $name(self, value: bool) -> Self {
59 self.set_flag(Self::$flag, value)
60 }
61 )*
62 }
63 };
64}
65
66flag_setters! {
67 read => FLAG_READ,
69 write => FLAG_WRITE,
71 create => FLAG_CREATE,
73 create_new => FLAG_CREATE_NEW,
75 truncate => FLAG_TRUNCATE,
77 append => FLAG_APPEND,
79}
80
81impl OpenOptions {
82 #[must_use]
84 pub fn is_read(&self) -> bool {
85 self.flags & Self::FLAG_READ != 0
86 }
87
88 #[must_use]
90 pub fn is_write(&self) -> bool {
91 self.flags & Self::FLAG_WRITE != 0
92 }
93
94 #[must_use]
96 pub fn is_create(&self) -> bool {
97 self.flags & Self::FLAG_CREATE != 0
98 }
99
100 #[must_use]
102 pub fn is_create_new(&self) -> bool {
103 self.flags & Self::FLAG_CREATE_NEW != 0
104 }
105
106 #[must_use]
108 pub fn is_truncate(&self) -> bool {
109 self.flags & Self::FLAG_TRUNCATE != 0
110 }
111
112 #[must_use]
114 pub fn is_append(&self) -> bool {
115 self.flags & Self::FLAG_APPEND != 0
116 }
117
118 #[must_use]
120 pub fn read_only() -> Self {
121 Self::new().read(true)
122 }
123
124 #[must_use]
126 pub fn create_write() -> Self {
127 Self::new().write(true).create(true).truncate(true)
128 }
129
130 #[must_use]
132 pub fn create_new_write() -> Self {
133 Self::new().write(true).create_new(true)
134 }
135}
136
137pub trait StorageProvider: Clone + Send + Sync + 'static {
141 type File: StorageFile + 'static;
143
144 fn open(
146 &self,
147 path: &str,
148 options: OpenOptions,
149 ) -> impl std::future::Future<Output = io::Result<Self::File>> + Send;
150
151 fn exists(&self, path: &str) -> impl std::future::Future<Output = io::Result<bool>> + Send;
153
154 fn delete(&self, path: &str) -> impl std::future::Future<Output = io::Result<()>> + Send;
156
157 fn rename(
159 &self,
160 from: &str,
161 to: &str,
162 ) -> impl std::future::Future<Output = io::Result<()>> + Send;
163}
164
165pub trait StorageFile: AsyncRead + AsyncWrite + AsyncSeek + Unpin + Send + Sync + 'static {
167 fn sync_all(&self) -> impl std::future::Future<Output = io::Result<()>> + Send;
169
170 fn sync_data(&self) -> impl std::future::Future<Output = io::Result<()>> + Send;
172
173 fn size(&self) -> impl std::future::Future<Output = io::Result<u64>> + Send;
175
176 fn set_len(&self, size: u64) -> impl std::future::Future<Output = io::Result<()>> + Send;
178}
179
180#[cfg(feature = "tokio-providers")]
182#[derive(Debug, Clone, Default)]
183pub struct TokioStorageProvider;
184
185#[cfg(feature = "tokio-providers")]
186impl TokioStorageProvider {
187 #[must_use]
189 pub fn new() -> Self {
190 Self
191 }
192}
193
194#[cfg(feature = "tokio-providers")]
195impl StorageProvider for TokioStorageProvider {
196 type File = TokioStorageFile;
197
198 async fn open(&self, path: &str, options: OpenOptions) -> io::Result<Self::File> {
199 let file = tokio::fs::OpenOptions::new()
200 .read(options.is_read())
201 .write(options.is_write())
202 .create(options.is_create())
203 .create_new(options.is_create_new())
204 .truncate(options.is_truncate())
205 .append(options.is_append())
206 .open(path)
207 .await?;
208 Ok(TokioStorageFile {
209 inner: file.compat(),
210 })
211 }
212
213 async fn exists(&self, path: &str) -> io::Result<bool> {
214 match tokio::fs::metadata(path).await {
215 Ok(_) => Ok(true),
216 Err(e) if e.kind() == io::ErrorKind::NotFound => Ok(false),
217 Err(e) => Err(e),
218 }
219 }
220
221 async fn delete(&self, path: &str) -> io::Result<()> {
222 tokio::fs::remove_file(path).await
223 }
224
225 async fn rename(&self, from: &str, to: &str) -> io::Result<()> {
226 tokio::fs::rename(from, to).await
227 }
228}
229
230#[cfg(feature = "tokio-providers")]
239#[derive(Debug)]
240pub struct TokioStorageFile {
241 inner: Compat<tokio::fs::File>,
242}
243
244#[cfg(feature = "tokio-providers")]
245impl StorageFile for TokioStorageFile {
246 async fn sync_all(&self) -> io::Result<()> {
247 self.inner.get_ref().sync_all().await
248 }
249
250 async fn sync_data(&self) -> io::Result<()> {
251 self.inner.get_ref().sync_data().await
252 }
253
254 async fn size(&self) -> io::Result<u64> {
255 let metadata = self.inner.get_ref().metadata().await?;
256 Ok(metadata.len())
257 }
258
259 async fn set_len(&self, size: u64) -> io::Result<()> {
260 self.inner.get_ref().set_len(size).await
261 }
262}
263
264#[cfg(feature = "tokio-providers")]
265impl AsyncRead for TokioStorageFile {
266 fn poll_read(
267 mut self: Pin<&mut Self>,
268 cx: &mut Context<'_>,
269 buf: &mut [u8],
270 ) -> Poll<io::Result<usize>> {
271 Pin::new(&mut self.inner).poll_read(cx, buf)
272 }
273}
274
275#[cfg(feature = "tokio-providers")]
276impl AsyncWrite for TokioStorageFile {
277 fn poll_write(
278 mut self: Pin<&mut Self>,
279 cx: &mut Context<'_>,
280 buf: &[u8],
281 ) -> Poll<io::Result<usize>> {
282 Pin::new(&mut self.inner).poll_write(cx, buf)
283 }
284
285 fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
286 Pin::new(&mut self.inner).poll_flush(cx)
287 }
288
289 fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
290 Pin::new(&mut self.inner).poll_close(cx)
291 }
292}
293
294#[cfg(feature = "tokio-providers")]
295impl AsyncSeek for TokioStorageFile {
296 fn poll_seek(
297 mut self: Pin<&mut Self>,
298 cx: &mut Context<'_>,
299 pos: SeekFrom,
300 ) -> Poll<io::Result<u64>> {
301 Pin::new(&mut self.inner).poll_seek(cx, pos)
302 }
303}