1use std::fmt::Debug;
7use std::io::SeekFrom;
8use std::pin::Pin;
9use std::time::{SystemTime, UNIX_EPOCH};
10
11use futures::{future, Future, Stream, TryFutureExt};
12use http::StatusCode;
13
14use crate::davpath::DavPath;
15
16macro_rules! notimplemented {
17 ($method:expr) => {
18 Err(FsError::NotImplemented)
19 };
20}
21
22macro_rules! notimplemented_fut {
23 ($method:expr) => {
24 Box::pin(future::ready(Err(FsError::NotImplemented)))
25 };
26}
27
28#[derive(Debug, Clone, Copy, PartialEq)]
32pub enum FsError {
33 NotImplemented,
35 GeneralFailure,
37 Exists,
39 NotFound,
41 Forbidden,
43 InsufficientStorage,
45 LoopDetected,
47 PathTooLong,
49 TooLarge,
51 IsRemote,
53}
54pub type FsResult<T> = std::result::Result<T, FsError>;
56
57#[derive(Debug, Clone)]
59pub struct DavProp {
60 pub name: String,
62 pub prefix: Option<String>,
64 pub namespace: Option<String>,
66 pub xml: Option<Vec<u8>>,
68}
69
70pub type FsFuture<'a, T> = Pin<Box<dyn Future<Output = FsResult<T>> + Send + 'a>>;
72pub type FsStream<T> = Pin<Box<dyn Stream<Item = T> + Send>>;
74
75#[derive(Debug, Clone, Copy, PartialEq, Eq)]
84pub enum ReadDirMeta {
85 Data,
87 DataSymlink,
89 None,
91}
92
93pub trait DavFileSystem: Sync + Send + BoxCloneFs {
95 fn open<'a>(&'a self, path: &'a DavPath, options: OpenOptions) -> FsFuture<Box<dyn DavFile>>;
97
98 fn read_dir<'a>(
100 &'a self,
101 path: &'a DavPath,
102 meta: ReadDirMeta,
103 ) -> FsFuture<FsStream<Box<dyn DavDirEntry>>>;
104
105 fn metadata<'a>(&'a self, path: &'a DavPath) -> FsFuture<Box<dyn DavMetaData>>;
107
108 #[allow(unused_variables)]
116 fn symlink_metadata<'a>(&'a self, path: &'a DavPath) -> FsFuture<Box<dyn DavMetaData>> {
117 self.metadata(path)
118 }
119
120 #[allow(unused_variables)]
124 fn create_dir<'a>(&'a self, path: &'a DavPath) -> FsFuture<()> {
125 notimplemented_fut!("create_dir")
126 }
127
128 #[allow(unused_variables)]
132 fn remove_dir<'a>(&'a self, path: &'a DavPath) -> FsFuture<()> {
133 notimplemented_fut!("remove_dir")
134 }
135
136 #[allow(unused_variables)]
140 fn remove_file<'a>(&'a self, path: &'a DavPath) -> FsFuture<()> {
141 notimplemented_fut!("remove_file")
142 }
143
144 #[allow(unused_variables)]
153 fn rename<'a>(&'a self, from: &'a DavPath, to: &'a DavPath) -> FsFuture<()> {
154 notimplemented_fut!("rename")
155 }
156
157 #[allow(unused_variables)]
164 fn copy<'a>(&'a self, from: &'a DavPath, to: &'a DavPath) -> FsFuture<()> {
165 notimplemented_fut!("copy")
166 }
167
168 #[doc(hidden)]
172 #[allow(unused_variables)]
173 fn set_accessed<'a>(&'a self, path: &'a DavPath, tm: SystemTime) -> FsFuture<()> {
174 notimplemented_fut!("set_accessed")
175 }
176
177 #[doc(hidden)]
181 #[allow(unused_variables)]
182 fn set_modified<'a>(&'a self, path: &'a DavPath, tm: SystemTime) -> FsFuture<()> {
183 notimplemented_fut!("set_mofified")
184 }
185
186 #[allow(unused_variables)]
190 fn have_props<'a>(&'a self, path: &'a DavPath) -> Pin<Box<dyn Future<Output = bool> + Send + 'a>> {
191 Box::pin(future::ready(false))
192 }
193
194 #[allow(unused_variables)]
198 fn patch_props<'a>(
199 &'a self,
200 path: &'a DavPath,
201 patch: Vec<(bool, DavProp)>,
202 ) -> FsFuture<Vec<(StatusCode, DavProp)>>
203 {
204 notimplemented_fut!("patch_props")
205 }
206
207 #[allow(unused_variables)]
211 fn get_props<'a>(&'a self, path: &'a DavPath, do_content: bool) -> FsFuture<Vec<DavProp>> {
212 notimplemented_fut!("get_props")
213 }
214
215 #[allow(unused_variables)]
219 fn get_prop<'a>(&'a self, path: &'a DavPath, prop: DavProp) -> FsFuture<Vec<u8>> {
220 notimplemented_fut!("get_prop`")
221 }
222
223 #[allow(unused_variables)]
231 fn get_quota<'a>(&'a self) -> FsFuture<(u64, Option<u64>)> {
232 notimplemented_fut!("get_quota`")
233 }
234}
235
236#[doc(hidden)]
238pub trait BoxCloneFs {
239 fn box_clone(&self) -> Box<dyn DavFileSystem>;
240}
241
242impl Clone for Box<dyn DavFileSystem> {
244 fn clone(&self) -> Box<dyn DavFileSystem> {
245 self.box_clone()
246 }
247}
248
249#[doc(hidden)]
251impl<FS: Clone + DavFileSystem + 'static> BoxCloneFs for FS {
252 fn box_clone(&self) -> Box<dyn DavFileSystem> {
253 Box::new((*self).clone())
254 }
255}
256
257pub trait DavDirEntry: Send + Sync {
259 fn name(&self) -> Vec<u8>;
261
262 fn metadata<'a>(&'a self) -> FsFuture<Box<dyn DavMetaData>>;
264
265 fn is_dir<'a>(&'a self) -> FsFuture<bool> {
270 Box::pin(self.metadata().and_then(|meta| future::ok(meta.is_dir())))
271 }
272
273 fn is_file<'a>(&'a self) -> FsFuture<bool> {
275 Box::pin(self.metadata().and_then(|meta| future::ok(meta.is_file())))
276 }
277
278 fn is_symlink<'a>(&'a self) -> FsFuture<bool> {
280 Box::pin(self.metadata().and_then(|meta| future::ok(meta.is_symlink())))
281 }
282}
283
284pub trait DavFile: Debug + Send + Sync {
287 fn metadata<'a>(&'a mut self) -> FsFuture<Box<dyn DavMetaData>>;
288 fn write_buf<'a>(&'a mut self, buf: Box<dyn bytes::Buf + Send>) -> FsFuture<()>;
289 fn write_bytes<'a>(&'a mut self, buf: bytes::Bytes) -> FsFuture<()>;
290 fn read_bytes<'a>(&'a mut self, count: usize) -> FsFuture<bytes::Bytes>;
291 fn seek<'a>(&'a mut self, pos: SeekFrom) -> FsFuture<u64>;
292 fn flush<'a>(&'a mut self) -> FsFuture<()>;
293}
294
295pub trait DavMetaData: Debug + BoxCloneMd + Send + Sync {
297 fn len(&self) -> u64;
299 fn modified(&self) -> FsResult<SystemTime>;
301 fn is_dir(&self) -> bool;
303
304 fn etag(&self) -> Option<String> {
309 if let Ok(t) = self.modified() {
310 if let Ok(t) = t.duration_since(UNIX_EPOCH) {
311 let t = t.as_secs() * 1000000 + t.subsec_nanos() as u64 / 1000;
312 let tag = if self.is_file() && self.len() > 0 {
313 format!("{:x}-{:x}", self.len(), t)
314 } else {
315 format!("{:x}", t)
316 };
317 return Some(tag);
318 }
319 }
320 None
321 }
322
323 fn is_file(&self) -> bool {
325 !self.is_dir()
326 }
327
328 fn is_symlink(&self) -> bool {
330 false
331 }
332
333 fn accessed(&self) -> FsResult<SystemTime> {
335 notimplemented!("access time")
336 }
337
338 fn created(&self) -> FsResult<SystemTime> {
340 notimplemented!("creation time")
341 }
342
343 fn status_changed(&self) -> FsResult<SystemTime> {
345 notimplemented!("status change time")
346 }
347
348 fn executable(&self) -> FsResult<bool> {
350 notimplemented!("executable")
351 }
352}
353
354impl Clone for Box<dyn DavMetaData> {
356 fn clone(&self) -> Box<dyn DavMetaData> {
357 self.box_clone()
358 }
359}
360
361#[doc(hidden)]
363pub trait BoxCloneMd {
364 fn box_clone(&self) -> Box<dyn DavMetaData>;
365}
366
367#[doc(hidden)]
369impl<MD: Clone + DavMetaData + 'static> BoxCloneMd for MD {
370 fn box_clone(&self) -> Box<dyn DavMetaData> {
371 Box::new((*self).clone())
372 }
373}
374
375#[derive(Debug, Clone, Copy, Default)]
377pub struct OpenOptions {
378 pub read: bool,
380 pub write: bool,
382 pub append: bool,
384 pub truncate: bool,
386 pub create: bool,
388 pub create_new: bool,
390}
391
392impl OpenOptions {
393 #[allow(dead_code)]
394 pub(crate) fn new() -> OpenOptions {
395 OpenOptions {
396 read: false,
397 write: false,
398 append: false,
399 truncate: false,
400 create: false,
401 create_new: false,
402 }
403 }
404
405 pub(crate) fn read() -> OpenOptions {
406 OpenOptions {
407 read: true,
408 write: false,
409 append: false,
410 truncate: false,
411 create: false,
412 create_new: false,
413 }
414 }
415
416 pub(crate) fn write() -> OpenOptions {
417 OpenOptions {
418 read: false,
419 write: true,
420 append: false,
421 truncate: false,
422 create: false,
423 create_new: false,
424 }
425 }
426}
427
428impl std::error::Error for FsError {
429 fn description(&self) -> &str {
430 "DavFileSystem error"
431 }
432 fn cause(&self) -> Option<&dyn std::error::Error> {
433 None
434 }
435}
436
437impl std::fmt::Display for FsError {
438 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
439 write!(f, "{:?}", self)
440 }
441}