1use std::fmt::Debug;
7use std::io::SeekFrom;
8use std::pin::Pin;
9use std::time::{SystemTime, UNIX_EPOCH};
10
11use dyn_clone::{clone_trait_object, DynClone};
12use futures_util::{future, Future, FutureExt, Stream, TryFutureExt};
13use http::StatusCode;
14
15use crate::davpath::DavPath;
16
17macro_rules! notimplemented {
18 ($method:expr) => {
19 Err(FsError::NotImplemented)
20 };
21}
22
23macro_rules! notimplemented_fut {
24 ($method:expr) => {
25 Box::pin(future::ready(Err(FsError::NotImplemented)))
26 };
27}
28
29#[derive(Debug, Clone, Copy, PartialEq)]
33pub enum FsError {
34 NotImplemented,
36 GeneralFailure,
38 Exists,
40 NotFound,
42 Forbidden,
44 InsufficientStorage,
46 LoopDetected,
48 PathTooLong,
50 TooLarge,
52 IsRemote,
54}
55pub type FsResult<T> = std::result::Result<T, FsError>;
57
58#[cfg(any(feature = "memfs", feature = "localfs"))]
59impl From<&std::io::Error> for FsError {
60 fn from(e: &std::io::Error) -> Self {
61 use std::io::ErrorKind;
62
63 if let Some(errno) = e.raw_os_error() {
64 match errno {
66 #[cfg(unix)]
67 libc::EMLINK | libc::ENOSPC | libc::EDQUOT => return FsError::InsufficientStorage,
68 #[cfg(windows)]
69 libc::EMLINK | libc::ENOSPC => return FsError::InsufficientStorage,
70 libc::EFBIG => return FsError::TooLarge,
71 libc::EACCES | libc::EPERM => return FsError::Forbidden,
72 libc::ENOTEMPTY | libc::EEXIST => return FsError::Exists,
73 libc::ELOOP => return FsError::LoopDetected,
74 libc::ENAMETOOLONG => return FsError::PathTooLong,
75 libc::ENOTDIR => return FsError::Forbidden,
76 libc::EISDIR => return FsError::Forbidden,
77 libc::EROFS => return FsError::Forbidden,
78 libc::ENOENT => return FsError::NotFound,
79 libc::ENOSYS => return FsError::NotImplemented,
80 libc::EXDEV => return FsError::IsRemote,
81 _ => {}
82 }
83 } else {
84 return FsError::NotImplemented;
87 }
88 match e.kind() {
90 ErrorKind::NotFound => FsError::NotFound,
91 ErrorKind::PermissionDenied => FsError::Forbidden,
92 _ => FsError::GeneralFailure,
93 }
94 }
95}
96
97#[cfg(any(feature = "memfs", feature = "localfs"))]
98impl From<std::io::Error> for FsError {
99 fn from(e: std::io::Error) -> Self {
100 (&e).into()
101 }
102}
103#[derive(Debug, Clone)]
105pub struct DavProp {
106 pub name: String,
108 pub prefix: Option<String>,
110 pub namespace: Option<String>,
112 pub xml: Option<Vec<u8>>,
114}
115
116pub type FsFuture<'a, T> = Pin<Box<dyn Future<Output = FsResult<T>> + Send + 'a>>;
118pub type FsStream<T> = Pin<Box<dyn Stream<Item = FsResult<T>> + Send>>;
120
121#[derive(Debug, Clone, Copy, PartialEq, Eq)]
130pub enum ReadDirMeta {
131 Data,
133 DataSymlink,
135 None,
137}
138
139pub trait DavFileSystem {
141 fn open<'a>(
143 &'a self,
144 path: &'a DavPath,
145 options: OpenOptions,
146 ) -> FsFuture<'a, Box<dyn DavFile>>;
147
148 fn read_dir<'a>(
150 &'a self,
151 path: &'a DavPath,
152 meta: ReadDirMeta,
153 ) -> FsFuture<'a, FsStream<Box<dyn DavDirEntry>>>;
154
155 fn metadata<'a>(&'a self, path: &'a DavPath) -> FsFuture<'a, Box<dyn DavMetaData>>;
157
158 #[allow(unused_variables)]
166 fn symlink_metadata<'a>(&'a self, path: &'a DavPath) -> FsFuture<'a, Box<dyn DavMetaData>> {
167 self.metadata(path)
168 }
169
170 #[allow(unused_variables)]
174 fn create_dir<'a>(&'a self, path: &'a DavPath) -> FsFuture<'a, ()> {
175 notimplemented_fut!("create_dir")
176 }
177
178 #[allow(unused_variables)]
182 fn remove_dir<'a>(&'a self, path: &'a DavPath) -> FsFuture<'a, ()> {
183 notimplemented_fut!("remove_dir")
184 }
185
186 #[allow(unused_variables)]
190 fn remove_file<'a>(&'a self, path: &'a DavPath) -> FsFuture<'a, ()> {
191 notimplemented_fut!("remove_file")
192 }
193
194 #[allow(unused_variables)]
203 fn rename<'a>(&'a self, from: &'a DavPath, to: &'a DavPath) -> FsFuture<'a, ()> {
204 notimplemented_fut!("rename")
205 }
206
207 #[allow(unused_variables)]
214 fn copy<'a>(&'a self, from: &'a DavPath, to: &'a DavPath) -> FsFuture<'a, ()> {
215 notimplemented_fut!("copy")
216 }
217
218 #[doc(hidden)]
222 #[allow(unused_variables)]
223 fn set_accessed<'a>(&'a self, path: &'a DavPath, tm: SystemTime) -> FsFuture<'a, ()> {
224 notimplemented_fut!("set_accessed")
225 }
226
227 #[doc(hidden)]
231 #[allow(unused_variables)]
232 fn set_modified<'a>(&'a self, path: &'a DavPath, tm: SystemTime) -> FsFuture<'a, ()> {
233 notimplemented_fut!("set_modified")
234 }
235
236 #[allow(unused_variables)]
240 fn have_props<'a>(
241 &'a self,
242 path: &'a DavPath,
243 ) -> Pin<Box<dyn Future<Output = bool> + Send + 'a>> {
244 Box::pin(future::ready(false))
245 }
246
247 #[allow(unused_variables)]
251 fn patch_props<'a>(
252 &'a self,
253 path: &'a DavPath,
254 patch: Vec<(bool, DavProp)>,
255 ) -> FsFuture<'a, Vec<(StatusCode, DavProp)>> {
256 notimplemented_fut!("patch_props")
257 }
258
259 #[allow(unused_variables)]
263 fn get_props<'a>(&'a self, path: &'a DavPath, do_content: bool) -> FsFuture<'a, Vec<DavProp>> {
264 notimplemented_fut!("get_props")
265 }
266
267 #[allow(unused_variables)]
271 fn get_prop<'a>(&'a self, path: &'a DavPath, prop: DavProp) -> FsFuture<'a, Vec<u8>> {
272 notimplemented_fut!("get_prop")
273 }
274
275 #[allow(unused_variables)]
283 fn get_quota(&self) -> FsFuture<(u64, Option<u64>)> {
284 notimplemented_fut!("get_quota")
285 }
286}
287
288pub trait GuardedFileSystem<C>: Send + Sync + DynClone
320where
321 C: Clone + Send + Sync + 'static,
322{
323 fn open<'a>(
325 &'a self,
326 path: &'a DavPath,
327 options: OpenOptions,
328 credentials: &'a C,
329 ) -> FsFuture<'a, Box<dyn DavFile>>;
330
331 fn read_dir<'a>(
333 &'a self,
334 path: &'a DavPath,
335 meta: ReadDirMeta,
336 credentials: &'a C,
337 ) -> FsFuture<'a, FsStream<Box<dyn DavDirEntry>>>;
338
339 fn metadata<'a>(
341 &'a self,
342 path: &'a DavPath,
343 credentials: &'a C,
344 ) -> FsFuture<'a, Box<dyn DavMetaData>>;
345
346 #[allow(unused_variables)]
354 fn symlink_metadata<'a>(
355 &'a self,
356 path: &'a DavPath,
357 credentials: &'a C,
358 ) -> FsFuture<'a, Box<dyn DavMetaData>> {
359 self.metadata(path, credentials)
360 }
361
362 #[allow(unused_variables)]
366 fn create_dir<'a>(&'a self, path: &'a DavPath, credentials: &'a C) -> FsFuture<'a, ()> {
367 notimplemented_fut!("create_dir")
368 }
369
370 #[allow(unused_variables)]
374 fn remove_dir<'a>(&'a self, path: &'a DavPath, credentials: &'a C) -> FsFuture<'a, ()> {
375 notimplemented_fut!("remove_dir")
376 }
377
378 #[allow(unused_variables)]
382 fn remove_file<'a>(&'a self, path: &'a DavPath, credentials: &'a C) -> FsFuture<'a, ()> {
383 notimplemented_fut!("remove_file")
384 }
385
386 #[allow(unused_variables)]
395 fn rename<'a>(
396 &'a self,
397 from: &'a DavPath,
398 to: &'a DavPath,
399 credentials: &'a C,
400 ) -> FsFuture<'a, ()> {
401 notimplemented_fut!("rename")
402 }
403
404 #[allow(unused_variables)]
411 fn copy<'a>(
412 &'a self,
413 from: &'a DavPath,
414 to: &'a DavPath,
415 credentials: &'a C,
416 ) -> FsFuture<'a, ()> {
417 notimplemented_fut!("copy")
418 }
419
420 #[doc(hidden)]
424 #[allow(unused_variables)]
425 fn set_accessed<'a>(
426 &'a self,
427 path: &'a DavPath,
428 tm: SystemTime,
429 credentials: &C,
430 ) -> FsFuture<'a, ()> {
431 notimplemented_fut!("set_accessed")
432 }
433
434 #[doc(hidden)]
438 #[allow(unused_variables)]
439 fn set_modified<'a>(
440 &'a self,
441 path: &'a DavPath,
442 tm: SystemTime,
443 credentials: &'a C,
444 ) -> FsFuture<'a, ()> {
445 notimplemented_fut!("set_mofified")
446 }
447
448 #[allow(unused_variables)]
452 fn have_props<'a>(
453 &'a self,
454 path: &'a DavPath,
455 credentials: &'a C,
456 ) -> Pin<Box<dyn Future<Output = bool> + Send + 'a>> {
457 Box::pin(future::ready(false))
458 }
459
460 #[allow(unused_variables)]
464 fn patch_props<'a>(
465 &'a self,
466 path: &'a DavPath,
467 patch: Vec<(bool, DavProp)>,
468 credentials: &'a C,
469 ) -> FsFuture<'a, Vec<(StatusCode, DavProp)>> {
470 notimplemented_fut!("patch_props")
471 }
472
473 #[allow(unused_variables)]
477 fn get_props<'a>(
478 &'a self,
479 path: &'a DavPath,
480 do_content: bool,
481 credentials: &'a C,
482 ) -> FsFuture<'a, Vec<DavProp>> {
483 notimplemented_fut!("get_props")
484 }
485
486 #[allow(unused_variables)]
490 fn get_prop<'a>(
491 &'a self,
492 path: &'a DavPath,
493 prop: DavProp,
494 credentials: &'a C,
495 ) -> FsFuture<'a, Vec<u8>> {
496 notimplemented_fut!("get_prop")
497 }
498
499 #[allow(unused_variables)]
507 fn get_quota<'a>(&'a self, credentials: &'a C) -> FsFuture<'a, (u64, Option<u64>)> {
508 notimplemented_fut!("get_quota")
509 }
510}
511
512clone_trait_object! {<C> GuardedFileSystem<C>}
513
514impl<Fs: DavFileSystem + Clone + Send + Sync> GuardedFileSystem<()> for Fs {
515 fn open<'a>(
516 &'a self,
517 path: &'a DavPath,
518 options: OpenOptions,
519 _credentials: &(),
520 ) -> FsFuture<'a, Box<dyn DavFile>> {
521 DavFileSystem::open(self, path, options)
522 }
523
524 fn read_dir<'a>(
525 &'a self,
526 path: &'a DavPath,
527 meta: ReadDirMeta,
528 _credentials: &(),
529 ) -> FsFuture<'a, FsStream<Box<dyn DavDirEntry>>> {
530 DavFileSystem::read_dir(self, path, meta)
531 }
532
533 fn metadata<'a>(
534 &'a self,
535 path: &'a DavPath,
536 _credentials: &(),
537 ) -> FsFuture<'a, Box<dyn DavMetaData>> {
538 DavFileSystem::metadata(self, path)
539 }
540
541 fn symlink_metadata<'a>(
542 &'a self,
543 path: &'a DavPath,
544 _credentials: &(),
545 ) -> FsFuture<'a, Box<dyn DavMetaData>> {
546 DavFileSystem::symlink_metadata(self, path)
547 }
548
549 fn create_dir<'a>(&'a self, path: &'a DavPath, _credentials: &()) -> FsFuture<'a, ()> {
550 DavFileSystem::create_dir(self, path)
551 }
552
553 fn remove_dir<'a>(&'a self, path: &'a DavPath, _credentials: &()) -> FsFuture<'a, ()> {
554 DavFileSystem::remove_dir(self, path)
555 }
556
557 fn remove_file<'a>(&'a self, path: &'a DavPath, _credentials: &()) -> FsFuture<'a, ()> {
558 DavFileSystem::remove_file(self, path)
559 }
560
561 fn rename<'a>(
562 &'a self,
563 from: &'a DavPath,
564 to: &'a DavPath,
565 _credentials: &(),
566 ) -> FsFuture<'a, ()> {
567 DavFileSystem::rename(self, from, to)
568 }
569
570 fn copy<'a>(
571 &'a self,
572 from: &'a DavPath,
573 to: &'a DavPath,
574 _credentials: &(),
575 ) -> FsFuture<'a, ()> {
576 DavFileSystem::copy(self, from, to)
577 }
578
579 fn set_accessed<'a>(
580 &'a self,
581 path: &'a DavPath,
582 tm: SystemTime,
583 _credentials: &(),
584 ) -> FsFuture<'a, ()> {
585 DavFileSystem::set_accessed(self, path, tm)
586 }
587
588 fn set_modified<'a>(
589 &'a self,
590 path: &'a DavPath,
591 tm: SystemTime,
592 _credentials: &(),
593 ) -> FsFuture<'a, ()> {
594 DavFileSystem::set_modified(self, path, tm)
595 }
596
597 fn have_props<'a>(
598 &'a self,
599 path: &'a DavPath,
600 _credentials: &(),
601 ) -> Pin<Box<dyn Future<Output = bool> + Send + 'a>> {
602 DavFileSystem::have_props(self, path)
603 }
604
605 fn patch_props<'a>(
606 &'a self,
607 path: &'a DavPath,
608 patch: Vec<(bool, DavProp)>,
609 _credentials: &(),
610 ) -> FsFuture<'a, Vec<(StatusCode, DavProp)>> {
611 DavFileSystem::patch_props(self, path, patch)
612 }
613
614 fn get_props<'a>(
615 &'a self,
616 path: &'a DavPath,
617 do_content: bool,
618 _credentials: &(),
619 ) -> FsFuture<'a, Vec<DavProp>> {
620 DavFileSystem::get_props(self, path, do_content)
621 }
622
623 fn get_prop<'a>(
624 &'a self,
625 path: &'a DavPath,
626 prop: DavProp,
627 _credentials: &(),
628 ) -> FsFuture<'a, Vec<u8>> {
629 DavFileSystem::get_prop(self, path, prop)
630 }
631
632 fn get_quota(&self, _credentials: &()) -> FsFuture<(u64, Option<u64>)> {
633 DavFileSystem::get_quota(self)
634 }
635}
636
637pub trait DavDirEntry: Send + Sync {
639 fn name(&self) -> Vec<u8>;
641
642 fn metadata(&self) -> FsFuture<Box<dyn DavMetaData>>;
644
645 fn is_dir(&self) -> FsFuture<bool> {
650 Box::pin(self.metadata().and_then(|meta| future::ok(meta.is_dir())))
651 }
652
653 fn is_file(&self) -> FsFuture<bool> {
655 Box::pin(self.metadata().and_then(|meta| future::ok(meta.is_file())))
656 }
657
658 fn is_symlink(&self) -> FsFuture<bool> {
660 Box::pin(
661 self.metadata()
662 .and_then(|meta| future::ok(meta.is_symlink())),
663 )
664 }
665}
666
667pub trait DavFile: Debug + Send + Sync {
670 fn metadata(&mut self) -> FsFuture<Box<dyn DavMetaData>>;
671 fn write_buf(&mut self, buf: Box<dyn bytes::Buf + Send>) -> FsFuture<()>;
672 fn write_bytes(&mut self, buf: bytes::Bytes) -> FsFuture<()>;
673 fn read_bytes(&mut self, count: usize) -> FsFuture<bytes::Bytes>;
674 fn seek(&mut self, pos: SeekFrom) -> FsFuture<u64>;
675 fn flush(&mut self) -> FsFuture<()>;
676 fn redirect_url(&mut self) -> FsFuture<Option<String>> {
677 future::ready(Ok(None)).boxed()
678 }
679}
680
681pub trait DavMetaData: Debug + Send + Sync + DynClone {
683 fn len(&self) -> u64;
685 fn modified(&self) -> FsResult<SystemTime>;
687 fn is_dir(&self) -> bool;
689
690 fn etag(&self) -> Option<String> {
695 if let Ok(t) = self.modified() {
696 if let Ok(t) = t.duration_since(UNIX_EPOCH) {
697 let t = t.as_secs() * 1000000 + t.subsec_nanos() as u64 / 1000;
698 let tag = if self.is_file() && self.len() > 0 {
699 format!("{:x}-{:x}", self.len(), t)
700 } else {
701 format!("{:x}", t)
702 };
703 return Some(tag);
704 }
705 }
706 None
707 }
708
709 fn is_file(&self) -> bool {
711 !self.is_dir()
712 }
713
714 fn is_symlink(&self) -> bool {
716 false
717 }
718
719 fn accessed(&self) -> FsResult<SystemTime> {
721 notimplemented!("access time")
722 }
723
724 fn created(&self) -> FsResult<SystemTime> {
726 notimplemented!("creation time")
727 }
728
729 fn status_changed(&self) -> FsResult<SystemTime> {
731 notimplemented!("status change time")
732 }
733
734 fn executable(&self) -> FsResult<bool> {
736 notimplemented!("executable")
737 }
738
739 fn is_empty(&self) -> bool {
741 self.len() == 0
742 }
743}
744
745clone_trait_object! {DavMetaData}
746
747#[derive(Debug, Clone, Default)]
749pub struct OpenOptions {
750 pub read: bool,
752 pub write: bool,
754 pub append: bool,
756 pub truncate: bool,
758 pub create: bool,
760 pub create_new: bool,
762 pub size: Option<u64>,
764 pub checksum: Option<String>,
766}
767
768impl OpenOptions {
769 #[allow(dead_code)]
770 pub(crate) fn new() -> OpenOptions {
771 OpenOptions {
772 read: false,
773 write: false,
774 append: false,
775 truncate: false,
776 create: false,
777 create_new: false,
778 size: None,
779 checksum: None,
780 }
781 }
782
783 pub(crate) fn read() -> OpenOptions {
784 OpenOptions {
785 read: true,
786 write: false,
787 append: false,
788 truncate: false,
789 create: false,
790 create_new: false,
791 size: None,
792 checksum: None,
793 }
794 }
795
796 pub(crate) fn write() -> OpenOptions {
797 OpenOptions {
798 read: false,
799 write: true,
800 append: false,
801 truncate: false,
802 create: false,
803 create_new: false,
804 size: None,
805 checksum: None,
806 }
807 }
808}
809
810impl std::error::Error for FsError {
811 fn description(&self) -> &str {
812 "DavFileSystem error"
813 }
814 fn cause(&self) -> Option<&dyn std::error::Error> {
815 None
816 }
817}
818
819impl std::fmt::Display for FsError {
820 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
821 write!(f, "{:?}", self)
822 }
823}