use std::fmt::Debug;
use std::io::SeekFrom;
use std::pin::Pin;
use std::time::{SystemTime, UNIX_EPOCH};
use futures::{future, Future, Stream, TryFutureExt};
use http::StatusCode;
use crate::davpath::DavPath;
macro_rules! notimplemented {
($method:expr) => {
Err(FsError::NotImplemented)
};
}
macro_rules! notimplemented_fut {
($method:expr) => {
Box::pin(future::ready(Err(FsError::NotImplemented)))
};
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum FsError {
NotImplemented,
GeneralFailure,
Exists,
NotFound,
Forbidden,
InsufficientStorage,
LoopDetected,
PathTooLong,
TooLarge,
IsRemote,
}
pub type FsResult<T> = std::result::Result<T, FsError>;
#[derive(Debug, Clone)]
pub struct DavProp {
pub name: String,
pub prefix: Option<String>,
pub namespace: Option<String>,
pub xml: Option<Vec<u8>>,
}
pub type FsFuture<'a, T> = Pin<Box<dyn Future<Output = FsResult<T>> + Send + 'a>>;
pub type FsStream<T> = Pin<Box<dyn Stream<Item = T> + Send>>;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ReadDirMeta {
Data,
DataSymlink,
None,
}
pub trait DavFileSystem: Sync + Send + BoxCloneFs {
fn open<'a>(&'a self, path: &'a DavPath, options: OpenOptions) -> FsFuture<Box<dyn DavFile>>;
fn read_dir<'a>(
&'a self,
path: &'a DavPath,
meta: ReadDirMeta,
) -> FsFuture<FsStream<Box<dyn DavDirEntry>>>;
fn metadata<'a>(&'a self, path: &'a DavPath) -> FsFuture<Box<dyn DavMetaData>>;
#[allow(unused_variables)]
fn symlink_metadata<'a>(&'a self, path: &'a DavPath) -> FsFuture<Box<dyn DavMetaData>> {
self.metadata(path)
}
#[allow(unused_variables)]
fn create_dir<'a>(&'a self, path: &'a DavPath) -> FsFuture<()> {
notimplemented_fut!("create_dir")
}
#[allow(unused_variables)]
fn remove_dir<'a>(&'a self, path: &'a DavPath) -> FsFuture<()> {
notimplemented_fut!("remove_dir")
}
#[allow(unused_variables)]
fn remove_file<'a>(&'a self, path: &'a DavPath) -> FsFuture<()> {
notimplemented_fut!("remove_file")
}
#[allow(unused_variables)]
fn rename<'a>(&'a self, from: &'a DavPath, to: &'a DavPath) -> FsFuture<()> {
notimplemented_fut!("rename")
}
#[allow(unused_variables)]
fn copy<'a>(&'a self, from: &'a DavPath, to: &'a DavPath) -> FsFuture<()> {
notimplemented_fut!("copy")
}
#[doc(hidden)]
#[allow(unused_variables)]
fn set_accessed<'a>(&'a self, path: &'a DavPath, tm: SystemTime) -> FsFuture<()> {
notimplemented_fut!("set_accessed")
}
#[doc(hidden)]
#[allow(unused_variables)]
fn set_modified<'a>(&'a self, path: &'a DavPath, tm: SystemTime) -> FsFuture<()> {
notimplemented_fut!("set_mofified")
}
#[allow(unused_variables)]
fn have_props<'a>(&'a self, path: &'a DavPath) -> Pin<Box<dyn Future<Output = bool> + Send + 'a>> {
Box::pin(future::ready(false))
}
#[allow(unused_variables)]
fn patch_props<'a>(
&'a self,
path: &'a DavPath,
patch: Vec<(bool, DavProp)>,
) -> FsFuture<Vec<(StatusCode, DavProp)>>
{
notimplemented_fut!("patch_props")
}
#[allow(unused_variables)]
fn get_props<'a>(&'a self, path: &'a DavPath, do_content: bool) -> FsFuture<Vec<DavProp>> {
notimplemented_fut!("get_props")
}
#[allow(unused_variables)]
fn get_prop<'a>(&'a self, path: &'a DavPath, prop: DavProp) -> FsFuture<Vec<u8>> {
notimplemented_fut!("get_prop`")
}
#[allow(unused_variables)]
fn get_quota<'a>(&'a self) -> FsFuture<(u64, Option<u64>)> {
notimplemented_fut!("get_quota`")
}
}
#[doc(hidden)]
pub trait BoxCloneFs {
fn box_clone(&self) -> Box<dyn DavFileSystem>;
}
impl Clone for Box<dyn DavFileSystem> {
fn clone(&self) -> Box<dyn DavFileSystem> {
self.box_clone()
}
}
#[doc(hidden)]
impl<FS: Clone + DavFileSystem + 'static> BoxCloneFs for FS {
fn box_clone(&self) -> Box<dyn DavFileSystem> {
Box::new((*self).clone())
}
}
pub trait DavDirEntry: Send + Sync {
fn name(&self) -> Vec<u8>;
fn metadata<'a>(&'a self) -> FsFuture<Box<dyn DavMetaData>>;
fn is_dir<'a>(&'a self) -> FsFuture<bool> {
Box::pin(self.metadata().and_then(|meta| future::ok(meta.is_dir())))
}
fn is_file<'a>(&'a self) -> FsFuture<bool> {
Box::pin(self.metadata().and_then(|meta| future::ok(meta.is_file())))
}
fn is_symlink<'a>(&'a self) -> FsFuture<bool> {
Box::pin(self.metadata().and_then(|meta| future::ok(meta.is_symlink())))
}
}
pub trait DavFile: Debug + Send + Sync {
fn metadata<'a>(&'a mut self) -> FsFuture<Box<dyn DavMetaData>>;
fn write_buf<'a>(&'a mut self, buf: Box<dyn bytes::Buf + Send>) -> FsFuture<()>;
fn write_bytes<'a>(&'a mut self, buf: bytes::Bytes) -> FsFuture<()>;
fn read_bytes<'a>(&'a mut self, count: usize) -> FsFuture<bytes::Bytes>;
fn seek<'a>(&'a mut self, pos: SeekFrom) -> FsFuture<u64>;
fn flush<'a>(&'a mut self) -> FsFuture<()>;
}
pub trait DavMetaData: Debug + BoxCloneMd + Send + Sync {
fn len(&self) -> u64;
fn modified(&self) -> FsResult<SystemTime>;
fn is_dir(&self) -> bool;
fn etag(&self) -> Option<String> {
if let Ok(t) = self.modified() {
if let Ok(t) = t.duration_since(UNIX_EPOCH) {
let t = t.as_secs() * 1000000 + t.subsec_nanos() as u64 / 1000;
let tag = if self.is_file() && self.len() > 0 {
format!("{:x}-{:x}", self.len(), t)
} else {
format!("{:x}", t)
};
return Some(tag);
}
}
None
}
fn is_file(&self) -> bool {
!self.is_dir()
}
fn is_symlink(&self) -> bool {
false
}
fn accessed(&self) -> FsResult<SystemTime> {
notimplemented!("access time")
}
fn created(&self) -> FsResult<SystemTime> {
notimplemented!("creation time")
}
fn status_changed(&self) -> FsResult<SystemTime> {
notimplemented!("status change time")
}
fn executable(&self) -> FsResult<bool> {
notimplemented!("executable")
}
}
impl Clone for Box<dyn DavMetaData> {
fn clone(&self) -> Box<dyn DavMetaData> {
self.box_clone()
}
}
#[doc(hidden)]
pub trait BoxCloneMd {
fn box_clone(&self) -> Box<dyn DavMetaData>;
}
#[doc(hidden)]
impl<MD: Clone + DavMetaData + 'static> BoxCloneMd for MD {
fn box_clone(&self) -> Box<dyn DavMetaData> {
Box::new((*self).clone())
}
}
#[derive(Debug, Clone, Copy, Default)]
pub struct OpenOptions {
pub read: bool,
pub write: bool,
pub append: bool,
pub truncate: bool,
pub create: bool,
pub create_new: bool,
}
impl OpenOptions {
#[allow(dead_code)]
pub(crate) fn new() -> OpenOptions {
OpenOptions {
read: false,
write: false,
append: false,
truncate: false,
create: false,
create_new: false,
}
}
pub(crate) fn read() -> OpenOptions {
OpenOptions {
read: true,
write: false,
append: false,
truncate: false,
create: false,
create_new: false,
}
}
pub(crate) fn write() -> OpenOptions {
OpenOptions {
read: false,
write: true,
append: false,
truncate: false,
create: false,
create_new: false,
}
}
}
impl std::error::Error for FsError {
fn description(&self) -> &str {
"DavFileSystem error"
}
fn cause(&self) -> Option<&dyn std::error::Error> {
None
}
}
impl std::fmt::Display for FsError {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "{:?}", self)
}
}