This is an automated email from the ASF dual-hosted git repository.

xuanwo pushed a commit to branch polish-webdav
in repository https://gitbox.apache.org/repos/asf/opendal.git

commit d9d9bc9b9442e899bda644129ad54ca965513274
Author: Xuanwo <[email protected]>
AuthorDate: Mon Jun 17 21:36:43 2024 +0800

    refactor(dav-server): Polish dav-server integration details
    
    Signed-off-by: Xuanwo <[email protected]>
---
 README.md                                          |  27 ++--
 core/README.md                                     |   2 +-
 integrations/dav-server/Cargo.toml                 |  13 +-
 integrations/dav-server/README.md                  |  58 +++++++-
 integrations/dav-server/src/dir.rs                 |  86 ++++++++++++
 integrations/dav-server/src/dir_entry.rs           |  54 --------
 integrations/dav-server/src/file.rs                | 148 +++++++++++++++------
 .../dav-server/src/{opendalfs.rs => fs.rs}         | 146 ++++++++++----------
 integrations/dav-server/src/lib.rs                 |  35 ++++-
 integrations/dav-server/src/metadata.rs            |  25 ++--
 integrations/dav-server/src/{lib.rs => utils.rs}   |  15 ++-
 integrations/object_store/README.md                |   4 +-
 12 files changed, 399 insertions(+), 214 deletions(-)

diff --git a/README.md b/README.md
index 7e45c69cf6..f6c662d94b 100644
--- a/README.md
+++ b/README.md
@@ -82,23 +82,30 @@ OpenDAL offers a unified data access layer, empowering 
users to seamlessly and e
 
 ## For *ANY* integrations
 
-| Name                   | Description                                         
 | Release                                                     |
-|------------------------|------------------------------------------------------|-------------------------------------------------------------|
-| [dav-server-opendalfs] | Access data via integrations to [dav-server-rs]     
 | [![dav-server-opendalfs image]][dav-server-opendalfs crate] |
-| [object_store_opendal] | Access data via integrations to [object_store]      
 | [![object_store_opendal image]][object_store_opendal crate] |
-| [fuse3_opendal]        | Access data via integrations to [fuse3]             
 | -                                                           |
-| [virtiofs_opendal]     | Access data via integrations to 
[vhost-user-backend] | -                                                        
   |
+| Name                   | Description                                         
 | Release                                     | Docs                           
                                                   |
+|------------------------|------------------------------------------------------|---------------------------------------------|-----------------------------------------------------------------------------------|
+| [dav-server-opendalfs] | a [dav-server-rs] implementation using opendal.     
 | [![dav-server image]][dav-server crate]     | [![Docs Release]][dav-server 
release docs] [![Docs Dev]][dav-server dev docs]     |
+| [object_store_opendal] | an [object_store] implementation using opendal.     
 | [![object_store image]][object_store crate] | [![Docs Release]][object_store 
release docs] [![Docs Dev]][object_store dev docs] |
+| [fuse3_opendal]        | Access data via integrations to [fuse3]             
 | -                                           | -                              
                                                   |
+| [virtiofs_opendal]     | Access data via integrations to 
[vhost-user-backend] | -                                           | -          
                                                                       |
 
 [dav-server-opendalfs]: integrations/dav-server/README.md
 [dav-server-rs]: https://github.com/messense/dav-server-rs
-[dav-server-opendalfs image]: 
https://img.shields.io/crates/v/dav-server-opendalfs.svg
-[dav-server-opendalfs crate]: https://crates.io/crates/dav-server-opendalfs
+[dav-server image]: https://img.shields.io/crates/v/dav-server-opendalfs.svg
+[dav-server crate]: https://crates.io/crates/dav-server-opendalfs
+[dav-server release docs]: https://docs.rs/dav-server-opendalfs/
+[dav-server dev docs]: 
https://opendal.apache.org/docs/dav-server-opendalfs/dav_server_opendalfs/
+
 [object_store_opendal]: integrations/object_store/README.md
 [object_store]: https://docs.rs/object_store
-[object_store_opendal image]: 
https://img.shields.io/crates/v/object_store_opendal.svg
-[object_store_opendal crate]: https://crates.io/crates/object_store_opendal
+[object_store image]: https://img.shields.io/crates/v/object_store_opendal.svg
+[object_store crate]: https://crates.io/crates/object_store_opendal
+[object_store release docs]: https://docs.rs/object_store_opendal/
+[object_store dev docs]: 
https://opendal.apache.org/docs/object-store-opendal/object_store_opendal/
+
 [fuse3_opendal]: integrations/fuse3/README.md
 [fuse3]: https://docs.rs/fuse3
+
 [virtiofs_opendal]: integrations/virtiofs/README.md
 [vhost-user-backend]: https://docs.rs/vhost-user-backend
 
diff --git a/core/README.md b/core/README.md
index 6b577d6064..94b3cc2ae3 100644
--- a/core/README.md
+++ b/core/README.md
@@ -16,7 +16,7 @@ OpenDAL offers a unified data access layer, empowering users 
to seamlessly and e
 
 ## Useful Links
 
-- Documentation: [stable](https://docs.rs/opendal/) | 
[main](https://opendal.apache.org/docs/rust/opendal/)
+- Documentation: [release](https://docs.rs/opendal/) | 
[dev](https://opendal.apache.org/docs/rust/opendal/)
 - [Release 
Notes](https://docs.rs/opendal/latest/opendal/docs/changelog/index.html)
 - [Upgrade 
Guide](https://docs.rs/opendal/latest/opendal/docs/upgrade/index.html)
 - [RFC List](https://docs.rs/opendal/latest/opendal/docs/rfcs/index.html)
diff --git a/integrations/dav-server/Cargo.toml 
b/integrations/dav-server/Cargo.toml
index 2d569b273a..0d31af23ee 100644
--- a/integrations/dav-server/Cargo.toml
+++ b/integrations/dav-server/Cargo.toml
@@ -30,19 +30,16 @@ rust-version = "1.75"
 [dependencies]
 anyhow = "1"
 bytes = { version = "1.4.0" }
-chrono = "0.4.28"
-dav-server = { version = "0.5.8" }
-dirs = "5.0.0"
+dav-server = { version = "0.6.0" }
 futures = "0.3"
-futures-util = { version = "0.3.16" }
+opendal = { version = "0.47.0", path = "../../core" }
+
+[dev-dependencies]
 opendal = { version = "0.47.0", path = "../../core", features = [
   "services-fs",
 ] }
-quick-xml = { version = "0.32", features = ["serialize", "overlapped-lists"] }
-serde = { version = "1", features = ["derive"] }
 tokio = { version = "1.27", features = [
-  "fs",
   "macros",
   "rt-multi-thread",
   "io-std",
-] }
+] }
\ No newline at end of file
diff --git a/integrations/dav-server/README.md 
b/integrations/dav-server/README.md
index f388319055..e16c6b5fe7 100644
--- a/integrations/dav-server/README.md
+++ b/integrations/dav-server/README.md
@@ -1,3 +1,57 @@
-# dav-server-opendalfs
+# Apache OpenDALâ„¢ dav-server integration
 
-[`dav-server-opendalfs`](https://crates.io/crates/dav-server-opendalfs) is an 
integration which uses OpenDAL as a backend to access data in various service 
with WebDAV protocol.
+[![Build Status]][actions] [![Latest Version]][crates.io] [![Crate 
Downloads]][crates.io] [![chat]][discord]
+
+[build status]: 
https://img.shields.io/github/actions/workflow/status/apache/opendal/ci_integration_dav_server.yml?branch=main
+[actions]: https://github.com/apache/opendal/actions?query=branch%3Amain
+[latest version]: https://img.shields.io/crates/v/dav-server-opendalfs.svg
+[crates.io]: https://crates.io/crates/dav-server-opendalfs
+[crate downloads]: https://img.shields.io/crates/d/dav-server-opendalfs.svg
+[chat]: https://img.shields.io/discord/1081052318650339399
+[discord]: https://opendal.apache.org/discord
+
+`dav-server-opendalfs` is an 
[`dav-server`](https://github.com/messense/dav-server-rs) implementation using 
opendal.
+
+This crate can help you to access ANY storage services with the same webdav 
API.
+
+## Useful Links
+
+- Documentation: [release](https://docs.rs/dav-server-opendalfs/) | 
[dev](https://opendal.apache.org/docs/dav-server-opendalfs/dav_server_opendalfs/)
+
+## Examples
+
+```
+use anyhow::Result;
+use dav_server::davpath::DavPath;
+use dav_server::fs::DavFileSystem;
+use dav_server_opendalfs::OpendalFs;
+use opendal::services::Memory;
+use opendal::Operator;
+
+#[tokio::test]
+async fn test() -> Result<()> {
+ let op = Operator::new(Memory::default())?.finish();
+
+ let webdavfs = OpendalFs::new(op);
+
+ let metadata = webdavfs
+     .metadata(&DavPath::new("/").unwrap())
+     .await
+     .unwrap();
+ println!("{}", metadata.is_dir());
+
+ Ok(())
+}
+```
+
+## Branding
+
+The first and most prominent mentions must use the full form: **Apache 
OpenDALâ„¢** of the name for any individual usage (webpage, handout, slides, 
etc.) Depending on the context and writing style, you should use the full form 
of the name sufficiently often to ensure that readers clearly understand the 
association of both the OpenDAL project and the OpenDAL software product to the 
ASF as the parent organization.
+
+For more details, see the [Apache Product Name Usage 
Guide](https://www.apache.org/foundation/marks/guide).
+
+## License and Trademarks
+
+Licensed under the Apache License, Version 2.0: 
http://www.apache.org/licenses/LICENSE-2.0
+
+Apache OpenDAL, OpenDAL, and Apache are either registered trademarks or 
trademarks of the Apache Software Foundation.
diff --git a/integrations/dav-server/src/dir.rs 
b/integrations/dav-server/src/dir.rs
new file mode 100644
index 0000000000..cb71a0304a
--- /dev/null
+++ b/integrations/dav-server/src/dir.rs
@@ -0,0 +1,86 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+use dav_server::fs::{DavDirEntry, DavMetaData};
+use futures::StreamExt;
+use futures::{FutureExt, Stream};
+use opendal::Operator;
+use opendal::{Entry, Lister};
+use std::pin::Pin;
+use std::task::Poll::Ready;
+use std::task::{ready, Context, Poll};
+
+use super::metadata::OpendalMetaData;
+use super::utils::*;
+
+/// OpendalStream is a stream of `DavDirEntry` that is used to list the 
contents of a directory.
+pub struct OpendalStream {
+    op: Operator,
+    lister: Lister,
+}
+
+impl OpendalStream {
+    /// Create a new opendal stream.
+    pub fn new(op: Operator, lister: Lister) -> Self {
+        OpendalStream { op, lister }
+    }
+}
+
+impl Stream for OpendalStream {
+    type Item = Box<dyn DavDirEntry>;
+
+    fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> 
Poll<Option<Self::Item>> {
+        let dav_stream = self.get_mut();
+        match ready!(dav_stream.lister.poll_next_unpin(cx)) {
+            Some(entry) => {
+                let webdav_entry = OpendalDirEntry::new(dav_stream.op.clone(), 
entry.unwrap());
+                Ready(Some(Box::new(webdav_entry) as Box<dyn DavDirEntry>))
+            }
+            None => Ready(None),
+        }
+    }
+}
+
+/// OpendalDirEntry is a `DavDirEntry` implementation for opendal.
+pub struct OpendalDirEntry {
+    op: Operator,
+    dir_entry: Entry,
+}
+
+impl OpendalDirEntry {
+    /// Create a new opendal dir entry.
+    pub fn new(op: Operator, dir_entry: Entry) -> Self {
+        OpendalDirEntry { dir_entry, op }
+    }
+}
+
+impl DavDirEntry for OpendalDirEntry {
+    fn name(&self) -> Vec<u8> {
+        self.dir_entry.name().as_bytes().to_vec()
+    }
+
+    fn metadata(&self) -> dav_server::fs::FsFuture<Box<dyn DavMetaData>> {
+        async move {
+            self.op
+                .stat(self.dir_entry.path())
+                .await
+                .map(|metadata| Box::new(OpendalMetaData::new(metadata)) as 
Box<dyn DavMetaData>)
+                .map_err(convert_error)
+        }
+        .boxed()
+    }
+}
diff --git a/integrations/dav-server/src/dir_entry.rs 
b/integrations/dav-server/src/dir_entry.rs
deleted file mode 100644
index 955266546c..0000000000
--- a/integrations/dav-server/src/dir_entry.rs
+++ /dev/null
@@ -1,54 +0,0 @@
-// Licensed to the Apache Software Foundation (ASF) under one
-// or more contributor license agreements.  See the NOTICE file
-// distributed with this work for additional information
-// regarding copyright ownership.  The ASF licenses this file
-// to you under the Apache License, Version 2.0 (the
-// "License"); you may not use this file except in compliance
-// with the License.  You may obtain a copy of the License at
-//
-//   http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing,
-// software distributed under the License is distributed on an
-// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-// KIND, either express or implied.  See the License for the
-// specific language governing permissions and limitations
-// under the License.
-
-use dav_server::fs::DavDirEntry;
-use futures::FutureExt;
-use opendal::Entry;
-use opendal::Operator;
-
-use super::file::convert_error;
-use super::metadata::WebdavMetaData;
-
-pub struct WebDAVDirEntry {
-    dir_entry: Entry,
-    op: Operator,
-}
-
-impl DavDirEntry for WebDAVDirEntry {
-    fn name(&self) -> Vec<u8> {
-        self.dir_entry.name().as_bytes().to_vec()
-    }
-
-    fn metadata(&self) -> dav_server::fs::FsFuture<Box<dyn 
dav_server::fs::DavMetaData>> {
-        async move {
-            self.op
-                .stat(self.dir_entry.path())
-                .await
-                .map(|metadata| {
-                    Box::new(WebdavMetaData::new(metadata)) as Box<dyn 
dav_server::fs::DavMetaData>
-                })
-                .map_err(convert_error)
-        }
-        .boxed()
-    }
-}
-
-impl WebDAVDirEntry {
-    pub fn new(dir_entry: Entry, op: Operator) -> Self {
-        WebDAVDirEntry { dir_entry, op }
-    }
-}
diff --git a/integrations/dav-server/src/file.rs 
b/integrations/dav-server/src/file.rs
index 2e19a6ec7e..d20a32c30e 100644
--- a/integrations/dav-server/src/file.rs
+++ b/integrations/dav-server/src/file.rs
@@ -15,85 +15,157 @@
 // specific language governing permissions and limitations
 // under the License.
 
+use std::fmt::{Debug, Formatter};
 use std::io::SeekFrom;
 
-use bytes::Bytes;
+use bytes::{Buf, Bytes, BytesMut};
 use dav_server::davpath::DavPath;
-use dav_server::fs::DavFile;
-use dav_server::fs::DavMetaData;
-use dav_server::fs::FsFuture;
+use dav_server::fs::{DavFile, OpenOptions};
+use dav_server::fs::{DavMetaData, FsResult};
+use dav_server::fs::{FsError, FsFuture};
 use futures::FutureExt;
-use opendal::Operator;
+use futures::{AsyncReadExt, AsyncSeekExt, AsyncWriteExt};
+use opendal::{FuturesAsyncReader, FuturesAsyncWriter, Operator};
 
-use super::metadata::WebdavMetaData;
+use super::metadata::OpendalMetaData;
+use super::utils::*;
 
-#[derive(Debug)]
-pub struct WebdavFile {
+/// OpendalFile is a `DavFile` implementation for opendal.
+pub struct OpendalFile {
     op: Operator,
     path: DavPath,
+    state: State,
+    buf: BytesMut,
 }
 
-impl WebdavFile {
-    pub fn new(op: Operator, path: DavPath) -> Self {
-        Self { op, path }
+impl Debug for OpendalFile {
+    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
+        f.debug_struct("OpendalFile")
+            .field("path", &self.path)
+            .field(
+                "state",
+                match &self.state {
+                    State::Read { .. } => &"read",
+                    State::Write(_) => &"write",
+                },
+            )
+            .finish()
     }
 }
 
-impl DavFile for WebdavFile {
-    fn read_bytes(&mut self, count: usize) -> FsFuture<Bytes> {
-        async move {
-            let file_path = self.path.as_url_string();
-            let buf = self
-                .op
-                .read_with(&file_path)
-                .range(0..count as u64)
+enum State {
+    Read(FuturesAsyncReader),
+    Write(FuturesAsyncWriter),
+}
+
+impl OpendalFile {
+    /// Create a new opendal file.
+    pub async fn open(op: Operator, path: DavPath, options: OpenOptions) -> 
FsResult<Self> {
+        let state = if options.read {
+            let r = op
+                .reader(path.as_url_string().as_str())
+                .await
+                .map_err(convert_error)?
+                .into_futures_async_read(..)
                 .await
                 .map_err(convert_error)?;
-            Ok(buf.to_bytes())
-        }
-        .boxed()
+            State::Read(r)
+        } else if options.write {
+            let w = op
+                .writer_with(path.as_url_string().as_str())
+                .append(options.append)
+                .await
+                .map_err(convert_error)?
+                .into_futures_async_write();
+            State::Write(w)
+        } else {
+            return Err(FsError::NotImplemented);
+        };
+
+        Ok(Self {
+            op,
+            path,
+            state,
+            buf: BytesMut::new(),
+        })
     }
+}
 
+impl DavFile for OpendalFile {
     fn metadata(&mut self) -> FsFuture<Box<dyn DavMetaData>> {
         async move {
             self.op
                 .stat(self.path.as_url_string().as_str())
                 .await
                 .map(|opendal_metadata| {
-                    Box::new(WebdavMetaData::new(opendal_metadata)) as Box<dyn 
DavMetaData>
+                    Box::new(OpendalMetaData::new(opendal_metadata)) as 
Box<dyn DavMetaData>
                 })
                 .map_err(convert_error)
         }
         .boxed()
     }
 
-    fn write_buf(&mut self, buf: Box<dyn bytes::Buf + Send>) -> FsFuture<()> {
-        self.write_bytes(Bytes::copy_from_slice(buf.chunk()))
+    fn write_buf(&mut self, mut buf: Box<dyn Buf + Send>) -> FsFuture<()> {
+        async move {
+            let State::Write(w) = &mut self.state else {
+                return Err(FsError::GeneralFailure);
+            };
+
+            w.write_all(&buf.copy_to_bytes(buf.remaining()))
+                .await
+                .map_err(|_| FsError::GeneralFailure)?;
+            Ok(())
+        }
+        .boxed()
     }
 
     fn write_bytes(&mut self, buf: Bytes) -> FsFuture<()> {
         async move {
-            let file_path = self.path.as_url_string();
-            self.op.write(&file_path, buf).await.map_err(convert_error)
+            let State::Write(w) = &mut self.state else {
+                return Err(FsError::GeneralFailure);
+            };
+
+            w.write_all(&buf).await.map_err(|_| FsError::GeneralFailure)
+        }
+        .boxed()
+    }
+
+    fn read_bytes(&mut self, count: usize) -> FsFuture<Bytes> {
+        async move {
+            let State::Read(r) = &mut self.state else {
+                return Err(FsError::GeneralFailure);
+            };
+
+            self.buf.resize(count, 0);
+            let len = r
+                .read(&mut self.buf)
+                .await
+                .map_err(|_| FsError::GeneralFailure)?;
+            Ok(self.buf.split_to(len).freeze())
         }
         .boxed()
     }
 
-    fn seek(&mut self, _pos: SeekFrom) -> FsFuture<u64> {
-        
futures_util::future::err(dav_server::fs::FsError::NotImplemented).boxed()
+    fn seek(&mut self, pos: SeekFrom) -> FsFuture<u64> {
+        async move {
+            let State::Read(r) = &mut self.state else {
+                return Err(FsError::GeneralFailure);
+            };
+
+            r.seek(pos).await.map_err(|_| FsError::GeneralFailure)
+        }
+        .boxed()
     }
 
     fn flush(&mut self) -> FsFuture<()> {
-        futures_util::future::ok(()).boxed()
-    }
-}
+        async move {
+            let State::Write(w) = &mut self.state else {
+                return Err(FsError::GeneralFailure);
+            };
 
-pub fn convert_error(opendal_error: opendal::Error) -> dav_server::fs::FsError 
{
-    match opendal_error.kind() {
-        opendal::ErrorKind::AlreadyExists | opendal::ErrorKind::IsSameFile => {
-            dav_server::fs::FsError::Exists
+            w.flush().await.map_err(|_| FsError::GeneralFailure)?;
+            w.close().await.map_err(|_| FsError::GeneralFailure)
         }
-        opendal::ErrorKind::NotFound => dav_server::fs::FsError::NotFound,
-        _ => dav_server::fs::FsError::GeneralFailure,
+        .boxed()
     }
 }
diff --git a/integrations/dav-server/src/opendalfs.rs 
b/integrations/dav-server/src/fs.rs
similarity index 66%
rename from integrations/dav-server/src/opendalfs.rs
rename to integrations/dav-server/src/fs.rs
index 92a2d77837..fda9575c79 100644
--- a/integrations/dav-server/src/opendalfs.rs
+++ b/integrations/dav-server/src/fs.rs
@@ -15,40 +15,67 @@
 // specific language governing permissions and limitations
 // under the License.
 
-use std::path::Path;
-use std::task::ready;
-use std::task::Poll::Ready;
-
 use dav_server::davpath::DavPath;
-use dav_server::fs::DavDirEntry;
-use dav_server::fs::DavFile;
-use dav_server::fs::DavFileSystem;
 use dav_server::fs::DavMetaData;
 use dav_server::fs::FsError;
+use dav_server::fs::{DavDirEntry, FsFuture};
+use dav_server::fs::{DavFile, FsStream};
+use dav_server::fs::{DavFileSystem, ReadDirMeta};
 use futures::FutureExt;
-use futures_util::Stream;
-use futures_util::StreamExt;
-use opendal::Lister;
+use futures::StreamExt;
 use opendal::Operator;
+use std::path::Path;
 
-use super::file::convert_error;
-use super::file::WebdavFile;
-use super::metadata::WebdavMetaData;
-use crate::dir_entry::WebDAVDirEntry;
-
+use super::dir::OpendalStream;
+use super::file::OpendalFile;
+use super::metadata::OpendalMetaData;
+use super::utils::convert_error;
+
+/// OpendalFs is a `DavFileSystem` implementation for opendal.
+///
+/// ```
+/// use anyhow::Result;
+/// use dav_server::davpath::DavPath;
+/// use dav_server::fs::DavFileSystem;
+/// use dav_server_opendalfs::OpendalFs;
+/// use opendal::services::Memory;
+/// use opendal::Operator;
+///
+/// #[tokio::test]
+/// async fn test() -> Result<()> {
+///     let op = Operator::new(Memory::default())?.finish();
+///
+///     let webdavfs = OpendalFs::new(op);
+///
+///     let metadata = webdavfs
+///         .metadata(&DavPath::new("/").unwrap())
+///         .await
+///         .unwrap();
+///     println!("{}", metadata.is_dir());
+///
+///     Ok(())
+/// }
+/// ```
 #[derive(Clone)]
 pub struct OpendalFs {
     pub op: Operator,
 }
 
+impl OpendalFs {
+    /// Create a new `OpendalFs` instance.
+    pub fn new(op: Operator) -> Box<OpendalFs> {
+        Box::new(OpendalFs { op })
+    }
+}
+
 impl DavFileSystem for OpendalFs {
     fn open<'a>(
         &'a self,
-        path: &'a dav_server::davpath::DavPath,
-        _options: dav_server::fs::OpenOptions,
-    ) -> dav_server::fs::FsFuture<Box<dyn dav_server::fs::DavFile>> {
+        path: &'a DavPath,
+        options: dav_server::fs::OpenOptions,
+    ) -> FsFuture<Box<dyn DavFile>> {
         async move {
-            let file = WebdavFile::new(self.op.clone(), path.clone());
+            let file = OpendalFile::open(self.op.clone(), path.clone(), 
options).await?;
             Ok(Box::new(file) as Box<dyn DavFile>)
         }
         .boxed()
@@ -56,29 +83,25 @@ impl DavFileSystem for OpendalFs {
 
     fn read_dir<'a>(
         &'a self,
-        path: &'a dav_server::davpath::DavPath,
-        _meta: dav_server::fs::ReadDirMeta,
-    ) -> dav_server::fs::FsFuture<dav_server::fs::FsStream<Box<dyn 
dav_server::fs::DavDirEntry>>>
-    {
+        path: &'a DavPath,
+        _meta: ReadDirMeta,
+    ) -> FsFuture<FsStream<Box<dyn DavDirEntry>>> {
         async move {
             self.op
                 .lister(path.as_url_string().as_str())
                 .await
-                .map(|lister| DavStream::new(self.op.clone(), lister).boxed())
+                .map(|lister| OpendalStream::new(self.op.clone(), 
lister).boxed())
                 .map_err(convert_error)
         }
         .boxed()
     }
 
-    fn metadata<'a>(
-        &'a self,
-        path: &'a dav_server::davpath::DavPath,
-    ) -> dav_server::fs::FsFuture<Box<dyn dav_server::fs::DavMetaData>> {
+    fn metadata<'a>(&'a self, path: &'a DavPath) -> FsFuture<Box<dyn 
DavMetaData>> {
         async move {
             let opendal_metadata = 
self.op.stat(path.as_url_string().as_str()).await;
             match opendal_metadata {
                 Ok(metadata) => {
-                    let webdav_metadata = WebdavMetaData::new(metadata);
+                    let webdav_metadata = OpendalMetaData::new(metadata);
                     Ok(Box::new(webdav_metadata) as Box<dyn DavMetaData>)
                 }
                 Err(e) => Err(convert_error(e)),
@@ -87,7 +110,7 @@ impl DavFileSystem for OpendalFs {
         .boxed()
     }
 
-    fn create_dir<'a>(&'a self, path: &'a DavPath) -> 
dav_server::fs::FsFuture<()> {
+    fn create_dir<'a>(&'a self, path: &'a DavPath) -> FsFuture<()> {
         async move {
             let path = path.as_url_string();
 
@@ -98,7 +121,7 @@ impl DavFileSystem for OpendalFs {
             match self.op.is_exist(parent.to_str().unwrap()).await {
                 Ok(exist) => {
                     if !exist && parent != Path::new("/") {
-                        return Err(dav_server::fs::FsError::NotFound);
+                        return Err(FsError::NotFound);
                     }
                 }
                 Err(e) => {
@@ -111,7 +134,7 @@ impl DavFileSystem for OpendalFs {
             let exist = self.op.is_exist(path).await;
             match exist {
                 Ok(exist) => match exist {
-                    true => Err(dav_server::fs::FsError::Exists),
+                    true => Err(FsError::Exists),
                     false => {
                         let res = self.op.create_dir(path).await;
                         match res {
@@ -126,7 +149,11 @@ impl DavFileSystem for OpendalFs {
         .boxed()
     }
 
-    fn remove_file<'a>(&'a self, path: &'a DavPath) -> 
dav_server::fs::FsFuture<()> {
+    fn remove_dir<'a>(&'a self, path: &'a DavPath) -> FsFuture<()> {
+        self.remove_file(path)
+    }
+
+    fn remove_file<'a>(&'a self, path: &'a DavPath) -> FsFuture<()> {
         async move {
             self.op
                 .delete(path.as_url_string().as_str())
@@ -136,75 +163,36 @@ impl DavFileSystem for OpendalFs {
         .boxed()
     }
 
-    fn remove_dir<'a>(&'a self, path: &'a DavPath) -> 
dav_server::fs::FsFuture<()> {
-        self.remove_file(path)
-    }
-
-    fn copy<'a>(&'a self, from: &'a DavPath, to: &'a DavPath) -> 
dav_server::fs::FsFuture<()> {
+    fn rename<'a>(&'a self, from: &'a DavPath, to: &'a DavPath) -> 
FsFuture<()> {
         async move {
             let from_path = from
                 .as_rel_ospath()
                 .to_str()
                 .ok_or(FsError::GeneralFailure)?;
             let to_path = 
to.as_rel_ospath().to_str().ok_or(FsError::GeneralFailure)?;
+            if from.is_collection() {
+                let _ = self.remove_file(to).await;
+            }
             self.op
-                .copy(from_path, to_path)
+                .rename(from_path, to_path)
                 .await
                 .map_err(convert_error)
         }
         .boxed()
     }
 
-    fn rename<'a>(&'a self, from: &'a DavPath, to: &'a DavPath) -> 
dav_server::fs::FsFuture<()> {
+    fn copy<'a>(&'a self, from: &'a DavPath, to: &'a DavPath) -> FsFuture<()> {
         async move {
             let from_path = from
                 .as_rel_ospath()
                 .to_str()
                 .ok_or(FsError::GeneralFailure)?;
             let to_path = 
to.as_rel_ospath().to_str().ok_or(FsError::GeneralFailure)?;
-            if from.is_collection() {
-                let _ = self.remove_file(to).await;
-            }
             self.op
-                .rename(from_path, to_path)
+                .copy(from_path, to_path)
                 .await
                 .map_err(convert_error)
         }
         .boxed()
     }
 }
-
-impl OpendalFs {
-    pub fn new(op: Operator) -> Box<OpendalFs> {
-        Box::new(OpendalFs { op })
-    }
-}
-
-struct DavStream {
-    op: Operator,
-    lister: Lister,
-}
-
-impl Stream for DavStream {
-    type Item = Box<dyn DavDirEntry>;
-
-    fn poll_next(
-        self: std::pin::Pin<&mut Self>,
-        cx: &mut std::task::Context<'_>,
-    ) -> std::task::Poll<Option<Self::Item>> {
-        let dav_stream = self.get_mut();
-        match ready!(dav_stream.lister.poll_next_unpin(cx)) {
-            Some(entry) => {
-                let webdav_entry = WebDAVDirEntry::new(entry.unwrap(), 
dav_stream.op.clone());
-                Ready(Some(Box::new(webdav_entry) as Box<dyn DavDirEntry>))
-            }
-            None => Ready(None),
-        }
-    }
-}
-
-impl DavStream {
-    fn new(op: Operator, lister: Lister) -> Self {
-        DavStream { op, lister }
-    }
-}
diff --git a/integrations/dav-server/src/lib.rs 
b/integrations/dav-server/src/lib.rs
index 6436fd7370..e818b2c128 100644
--- a/integrations/dav-server/src/lib.rs
+++ b/integrations/dav-server/src/lib.rs
@@ -15,9 +15,38 @@
 // specific language governing permissions and limitations
 // under the License.
 
-mod dir_entry;
+//! dav-server-opendalfs is an dav-server implementation using opendal.
+//!
+//! This crate can help you to access ANY storage services with the same 
webdav API.
+//!
+//! ```
+//! use anyhow::Result;
+//! use dav_server::davpath::DavPath;
+//! use dav_server::fs::DavFileSystem;
+//! use dav_server_opendalfs::OpendalFs;
+//! use opendal::services::Memory;
+//! use opendal::Operator;
+//!
+//! #[tokio::test]
+//! async fn test() -> Result<()> {
+//!     let op = Operator::new(Memory::default())?.finish();
+//!
+//!     let webdavfs = OpendalFs::new(op);
+//!
+//!     let metadata = webdavfs
+//!         .metadata(&DavPath::new("/").unwrap())
+//!         .await
+//!         .unwrap();
+//!     println!("{}", metadata.is_dir());
+//!
+//!     Ok(())
+//! }
+//! ```
+
+mod dir;
 mod file;
 mod metadata;
-mod opendalfs;
+mod utils;
 
-pub use opendalfs::OpendalFs;
+mod fs;
+pub use fs::OpendalFs;
diff --git a/integrations/dav-server/src/metadata.rs 
b/integrations/dav-server/src/metadata.rs
index 03295125d5..16099efd39 100644
--- a/integrations/dav-server/src/metadata.rs
+++ b/integrations/dav-server/src/metadata.rs
@@ -15,27 +15,30 @@
 // specific language governing permissions and limitations
 // under the License.
 
-use dav_server::fs::DavMetaData;
 use dav_server::fs::FsError;
+use dav_server::fs::{DavMetaData, FsResult};
 use opendal::Metadata;
+use std::time::SystemTime;
 
+/// OpendalMetaData is a `DavMetaData` implementation for opendal.
 #[derive(Debug, Clone)]
-pub struct WebdavMetaData {
+pub struct OpendalMetaData {
     metadata: Metadata,
 }
 
-impl WebdavMetaData {
+impl OpendalMetaData {
+    /// Create a new opendal metadata.
     pub fn new(metadata: Metadata) -> Self {
-        WebdavMetaData { metadata }
+        OpendalMetaData { metadata }
     }
 }
 
-impl DavMetaData for WebdavMetaData {
+impl DavMetaData for OpendalMetaData {
     fn len(&self) -> u64 {
         self.metadata.content_length()
     }
 
-    fn modified(&self) -> dav_server::fs::FsResult<std::time::SystemTime> {
+    fn modified(&self) -> FsResult<SystemTime> {
         match self.metadata.last_modified() {
             Some(t) => Ok(t.into()),
             None => Err(FsError::GeneralFailure),
@@ -46,15 +49,15 @@ impl DavMetaData for WebdavMetaData {
         self.metadata.is_dir()
     }
 
-    fn is_file(&self) -> bool {
-        self.metadata.is_file()
-    }
-
     fn etag(&self) -> Option<String> {
         self.metadata.etag().map(|s| s.to_string())
     }
 
-    fn status_changed(&self) -> 
dav_server::fs::FsResult<std::time::SystemTime> {
+    fn is_file(&self) -> bool {
+        self.metadata.is_file()
+    }
+
+    fn status_changed(&self) -> FsResult<SystemTime> {
         self.metadata
             .last_modified()
             .map_or(Err(FsError::GeneralFailure), |t| Ok(t.into()))
diff --git a/integrations/dav-server/src/lib.rs 
b/integrations/dav-server/src/utils.rs
similarity index 67%
copy from integrations/dav-server/src/lib.rs
copy to integrations/dav-server/src/utils.rs
index 6436fd7370..132712f26c 100644
--- a/integrations/dav-server/src/lib.rs
+++ b/integrations/dav-server/src/utils.rs
@@ -15,9 +15,12 @@
 // specific language governing permissions and limitations
 // under the License.
 
-mod dir_entry;
-mod file;
-mod metadata;
-mod opendalfs;
-
-pub use opendalfs::OpendalFs;
+pub fn convert_error(opendal_error: opendal::Error) -> dav_server::fs::FsError 
{
+    match opendal_error.kind() {
+        opendal::ErrorKind::AlreadyExists | opendal::ErrorKind::IsSameFile => {
+            dav_server::fs::FsError::Exists
+        }
+        opendal::ErrorKind::NotFound => dav_server::fs::FsError::NotFound,
+        _ => dav_server::fs::FsError::GeneralFailure,
+    }
+}
diff --git a/integrations/object_store/README.md 
b/integrations/object_store/README.md
index b8ecf3f531..24465ed95b 100644
--- a/integrations/object_store/README.md
+++ b/integrations/object_store/README.md
@@ -14,10 +14,10 @@
 
 This crate can help you to access 30 more storage services with the same 
object_store API.
 
-
+    
 ## Useful Links
 
-- Documentation: [stable](https://docs.rs/object_store_opendal/) | 
[main](https://opendal.apache.org/docs/object-store-opendal/object_store_opendal/)
+- Documentation: [release](https://docs.rs/object_store_opendal/) | 
[dev](https://opendal.apache.org/docs/object-store-opendal/object_store_opendal/)
 
 ## Examples
 

Reply via email to