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

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


The following commit(s) were added to refs/heads/main by this push:
     new 0920ac379 refactor: List must support list without recursive (#3721)
0920ac379 is described below

commit 0920ac37909eeaf5d18e15c98df860bf5ff47c15
Author: Xuanwo <[email protected]>
AuthorDate: Thu Dec 7 16:44:30 2023 +0800

    refactor: List must support list without recursive (#3721)
    
    * List must support list without recursive
    
    Signed-off-by: Xuanwo <[email protected]>
    
    * Add list nest dir test
    
    Signed-off-by: Xuanwo <[email protected]>
    
    * Remove non-native feature
    
    Signed-off-by: Xuanwo <[email protected]>
    
    * Remove non-native feature
    
    Signed-off-by: Xuanwo <[email protected]>
    
    * Fix kv
    
    Signed-off-by: Xuanwo <[email protected]>
    
    * Fix java
    
    Signed-off-by: Xuanwo <[email protected]>
    
    * Fix list
    
    Signed-off-by: Xuanwo <[email protected]>
    
    * Fix lister
    
    Signed-off-by: Xuanwo <[email protected]>
    
    * Fix binding c
    
    Signed-off-by: Xuanwo <[email protected]>
    
    * Fix nodejs
    
    Signed-off-by: Xuanwo <[email protected]>
    
    ---------
    
    Signed-off-by: Xuanwo <[email protected]>
---
 bindings/c/include/opendal.h                       |  4 --
 bindings/c/src/operator_info.rs                    |  3 -
 bindings/c/tests/bdd.cpp                           | 20 ------
 bindings/c/tests/opinfo.cpp                        |  4 --
 bindings/java/src/lib.rs                           |  3 +-
 .../main/java/org/apache/opendal/Capability.java   | 15 ++--
 bindings/nodejs/generated.d.ts                     |  2 -
 bindings/nodejs/src/capability.rs                  |  6 --
 bindings/python/src/capability.rs                  |  3 -
 core/src/layers/complete.rs                        | 55 +++------------
 core/src/layers/immutable_index.rs                 |  1 -
 core/src/layers/retry.rs                           |  1 -
 core/src/raw/adapters/kv/backend.rs                | 81 ++--------------------
 core/src/raw/adapters/typed_kv/backend.rs          | 78 ++-------------------
 core/src/raw/oio/list/flat_list.rs                 | 10 ---
 core/src/raw/oio/list/hierarchy_list.rs            | 18 ++++-
 core/src/services/alluxio/backend.rs               |  1 -
 core/src/services/azblob/backend.rs                |  1 -
 core/src/services/azdls/backend.rs                 |  1 -
 core/src/services/azfile/backend.rs                |  1 -
 core/src/services/b2/backend.rs                    |  1 -
 core/src/services/cos/backend.rs                   |  1 -
 core/src/services/dashmap/backend.rs               | 12 +++-
 core/src/services/dbfs/backend.rs                  |  1 -
 core/src/services/fs/backend.rs                    |  1 -
 core/src/services/ftp/backend.rs                   |  1 -
 core/src/services/gcs/backend.rs                   |  1 -
 core/src/services/gdrive/backend.rs                |  1 -
 core/src/services/hdfs/backend.rs                  |  1 -
 core/src/services/huggingface/backend.rs           |  1 -
 core/src/services/ipfs/backend.rs                  |  1 -
 core/src/services/ipmfs/backend.rs                 |  1 -
 core/src/services/obs/backend.rs                   |  1 -
 core/src/services/onedrive/backend.rs              |  1 -
 core/src/services/oss/backend.rs                   |  1 -
 core/src/services/s3/backend.rs                    |  1 -
 core/src/services/sftp/backend.rs                  |  1 -
 core/src/services/swift/backend.rs                 |  1 -
 core/src/services/webdav/backend.rs                |  1 -
 core/src/services/webhdfs/backend.rs               |  1 -
 core/src/types/capability.rs                       |  2 -
 core/tests/behavior/list.rs                        |  8 ++-
 42 files changed, 62 insertions(+), 287 deletions(-)

diff --git a/bindings/c/include/opendal.h b/bindings/c/include/opendal.h
index 38b3e531e..36dab301f 100644
--- a/bindings/c/include/opendal.h
+++ b/bindings/c/include/opendal.h
@@ -635,10 +635,6 @@ typedef struct opendal_capability {
    * If backend supports list with start after.
    */
   bool list_with_start_after;
-  /**
-   * If backend support list with using slash as delimiter.
-   */
-  bool list_without_recursive;
   /**
    * If backend supports list without delimiter.
    */
diff --git a/bindings/c/src/operator_info.rs b/bindings/c/src/operator_info.rs
index 4d805e4de..6c7fc69d1 100644
--- a/bindings/c/src/operator_info.rs
+++ b/bindings/c/src/operator_info.rs
@@ -116,8 +116,6 @@ pub struct opendal_capability {
     pub list_with_limit: bool,
     /// If backend supports list with start after.
     pub list_with_start_after: bool,
-    /// If backend support list with using slash as delimiter.
-    pub list_without_recursive: bool,
     /// If backend supports list without delimiter.
     pub list_with_recursive: bool,
 
@@ -266,7 +264,6 @@ impl From<core::Capability> for opendal_capability {
             list_with_limit: value.list_with_limit,
             list_with_start_after: value.list_with_start_after,
             list_with_recursive: value.list_with_recursive,
-            list_without_recursive: value.list_without_recursive,
             presign: value.presign,
             presign_read: value.presign_read,
             presign_stat: value.presign_stat,
diff --git a/bindings/c/tests/bdd.cpp b/bindings/c/tests/bdd.cpp
index 67b8f56b8..370c17fd5 100644
--- a/bindings/c/tests/bdd.cpp
+++ b/bindings/c/tests/bdd.cpp
@@ -75,26 +75,6 @@ TEST_F(OpendalBddTest, FeatureTest)
     opendal_metadata* meta = s.meta;
     EXPECT_TRUE(opendal_metadata_is_file(meta));
 
-    // The blocking file "test" should be renamed to "test-copy"
-    error = opendal_operator_rename(this->p, this->path.c_str(), "test-copy");
-    EXPECT_EQ(error, nullptr);
-    e = opendal_operator_is_exist(this->p, "test-copy");
-    EXPECT_EQ(e.error, nullptr);
-    EXPECT_TRUE(e.is_exist);
-    e = opendal_operator_is_exist(this->p, this->path.c_str());
-    EXPECT_EQ(e.error, nullptr);
-    EXPECT_FALSE(e.is_exist);
-
-    // The blocking file "test-copy" should be copied to "test"
-    error = opendal_operator_copy(this->p, "test-copy", this->path.c_str());
-    EXPECT_EQ(error, nullptr);
-    e = opendal_operator_is_exist(this->p, this->path.c_str());
-    EXPECT_EQ(e.error, nullptr);
-    EXPECT_TRUE(e.is_exist);
-    e = opendal_operator_is_exist(this->p, "test-copy");
-    EXPECT_EQ(e.error, nullptr);
-    EXPECT_TRUE(e.is_exist);
-
     // The blocking file "test" content length must be 13
     EXPECT_EQ(opendal_metadata_content_length(meta), 13);
 
diff --git a/bindings/c/tests/opinfo.cpp b/bindings/c/tests/opinfo.cpp
index 480364017..42c70fde2 100644
--- a/bindings/c/tests/opinfo.cpp
+++ b/bindings/c/tests/opinfo.cpp
@@ -75,8 +75,6 @@ TEST_F(OpendalOperatorInfoTest, CapabilityTest)
     EXPECT_TRUE(full_cap.delete_);
     EXPECT_TRUE(full_cap.list);
     EXPECT_TRUE(full_cap.list_with_recursive);
-    EXPECT_TRUE(full_cap.copy);
-    EXPECT_TRUE(full_cap.rename);
 
     EXPECT_TRUE(native_cap.blocking);
     EXPECT_TRUE(native_cap.read);
@@ -90,8 +88,6 @@ TEST_F(OpendalOperatorInfoTest, CapabilityTest)
     EXPECT_TRUE(native_cap.delete_);
     EXPECT_TRUE(native_cap.list);
     EXPECT_TRUE(native_cap.list_with_recursive);
-    EXPECT_TRUE(native_cap.copy);
-    EXPECT_TRUE(native_cap.rename);
 }
 
 TEST_F(OpendalOperatorInfoTest, InfoTest)
diff --git a/bindings/java/src/lib.rs b/bindings/java/src/lib.rs
index b3631d14e..d95c0debd 100644
--- a/bindings/java/src/lib.rs
+++ b/bindings/java/src/lib.rs
@@ -153,7 +153,7 @@ fn make_operator_info<'a>(env: &mut JNIEnv<'a>, info: 
OperatorInfo) -> Result<JO
 fn make_capability<'a>(env: &mut JNIEnv<'a>, cap: Capability) -> 
Result<JObject<'a>> {
     let capability = env.new_object(
         "org/apache/opendal/Capability",
-        "(ZZZZZZZZZZZZZZZZZZJJJZZZZZZZZZZZZZZZJZ)V",
+        "(ZZZZZZZZZZZZZZZZZZJJJZZZZZZZZZZZZZZJZ)V",
         &[
             JValue::Bool(cap.stat as jboolean),
             JValue::Bool(cap.stat_with_if_match as jboolean),
@@ -183,7 +183,6 @@ fn make_capability<'a>(env: &mut JNIEnv<'a>, cap: 
Capability) -> Result<JObject<
             JValue::Bool(cap.list as jboolean),
             JValue::Bool(cap.list_with_limit as jboolean),
             JValue::Bool(cap.list_with_start_after as jboolean),
-            JValue::Bool(cap.list_without_recursive as jboolean),
             JValue::Bool(cap.list_with_recursive as jboolean),
             JValue::Bool(cap.presign as jboolean),
             JValue::Bool(cap.presign_read as jboolean),
diff --git a/bindings/java/src/main/java/org/apache/opendal/Capability.java 
b/bindings/java/src/main/java/org/apache/opendal/Capability.java
index dfde61276..45a76e2b7 100644
--- a/bindings/java/src/main/java/org/apache/opendal/Capability.java
+++ b/bindings/java/src/main/java/org/apache/opendal/Capability.java
@@ -167,14 +167,9 @@ public class Capability {
     public final boolean listWithStartAfter;
 
     /**
-     * If backend support list with using slash as delimiter.
+     * If backend support list with recursive.
      */
-    public final boolean listWithDelimiterSlash;
-
-    /**
-     * If backend supports list without delimiter.
-     */
-    public final boolean listWithoutDelimiter;
+    public final boolean listWithRecursive;
 
     /**
      * If operator supports presign.
@@ -245,8 +240,7 @@ public class Capability {
             boolean list,
             boolean listWithLimit,
             boolean listWithStartAfter,
-            boolean listWithDelimiterSlash,
-            boolean listWithoutDelimiter,
+            boolean listWithRecursive,
             boolean presign,
             boolean presignRead,
             boolean presignStat,
@@ -283,8 +277,7 @@ public class Capability {
         this.list = list;
         this.listWithLimit = listWithLimit;
         this.listWithStartAfter = listWithStartAfter;
-        this.listWithDelimiterSlash = listWithDelimiterSlash;
-        this.listWithoutDelimiter = listWithoutDelimiter;
+        this.listWithRecursive = listWithRecursive;
         this.presign = presign;
         this.presignRead = presignRead;
         this.presignStat = presignStat;
diff --git a/bindings/nodejs/generated.d.ts b/bindings/nodejs/generated.d.ts
index c5e1d1ba8..9f45b248d 100644
--- a/bindings/nodejs/generated.d.ts
+++ b/bindings/nodejs/generated.d.ts
@@ -128,8 +128,6 @@ export class Capability {
   get listWithStartAfter(): boolean
   /** If backend supports list with recursive. */
   get listWithRecursive(): boolean
-  /** If backend supports list without recursive. */
-  get listWithoutRecursive(): boolean
   /** If operator supports presign. */
   get presign(): boolean
   /** If operator supports presign read. */
diff --git a/bindings/nodejs/src/capability.rs 
b/bindings/nodejs/src/capability.rs
index 8084eb796..742f3c9a6 100644
--- a/bindings/nodejs/src/capability.rs
+++ b/bindings/nodejs/src/capability.rs
@@ -230,12 +230,6 @@ impl Capability {
         self.0.list_with_recursive
     }
 
-    /// If backend supports list without recursive.
-    #[napi(getter)]
-    pub fn list_without_recursive(&self) -> bool {
-        self.0.list_without_recursive
-    }
-
     /// If operator supports presign.
     #[napi(getter)]
     pub fn presign(&self) -> bool {
diff --git a/bindings/python/src/capability.rs 
b/bindings/python/src/capability.rs
index 533d016fa..723949272 100644
--- a/bindings/python/src/capability.rs
+++ b/bindings/python/src/capability.rs
@@ -96,8 +96,6 @@ pub struct Capability {
     pub list_with_limit: bool,
     /// If backend supports list with start after.
     pub list_with_start_after: bool,
-    /// If backend support list with using slash as delimiter.
-    pub list_without_recursive: bool,
     /// If backend supports list without delimiter.
     pub list_with_recursive: bool,
 
@@ -155,7 +153,6 @@ impl Capability {
             list: capability.list,
             list_with_limit: capability.list_with_limit,
             list_with_start_after: capability.list_with_start_after,
-            list_without_recursive: capability.list_without_recursive,
             list_with_recursive: capability.list_with_recursive,
             presign: capability.presign,
             presign_read: capability.presign_read,
diff --git a/core/src/layers/complete.rs b/core/src/layers/complete.rs
index a536c6ca3..278a9e827 100644
--- a/core/src/layers/complete.rs
+++ b/core/src/layers/complete.rs
@@ -29,7 +29,6 @@ use bytes::Bytes;
 
 use crate::raw::oio::FileReader;
 use crate::raw::oio::FlatLister;
-use crate::raw::oio::HierarchyLister;
 use crate::raw::oio::LazyReader;
 use crate::raw::oio::RangeReader;
 use crate::raw::oio::StreamableReader;
@@ -366,33 +365,18 @@ impl<A: Accessor> CompleteAccessor<A> {
 
         let recursive = args.recursive();
 
-        match (
-            recursive,
-            cap.list_with_recursive,
-            cap.list_without_recursive,
-        ) {
-            // - If service can both list_with_recursive and 
list_without_recursive
-            // - If recursive is true while services can list_with_recursive
-            // - If recursive is false while services can 
list_without_recursive
-            (_, true, true) | (true, true, _) | (false, _, true) => {
+        match (recursive, cap.list_with_recursive) {
+            // - If service can list_with_recursive
+            // - If recursive is false
+            (_, true) | (false, _) => {
                 let (rp, p) = self.inner.list(path, args).await?;
                 Ok((rp, CompleteLister::AlreadyComplete(p)))
             }
-            // If services can't list_with_recursive nor 
list_without_recursive.
-            //
-            // It should be a service level bug.
-            (_, false, false) => 
Err(self.new_unsupported_error(Operation::List)),
             // If recursive is true but service can't list_with_recursive
-            (true, false, true) => {
+            (true, false) => {
                 let p = FlatLister::new(self.inner.clone(), path);
                 Ok((RpList::default(), CompleteLister::NeedFlat(p)))
             }
-            // If recursive is false but service can't list_without_recursive
-            (false, true, false) => {
-                let (_, p) = self.inner.list(path, 
args.with_recursive(true)).await?;
-                let p = HierarchyLister::new(p, path);
-                Ok((RpList::default(), CompleteLister::NeedHierarchy(p)))
-            }
         }
     }
 
@@ -408,34 +392,18 @@ impl<A: Accessor> CompleteAccessor<A> {
 
         let recursive = args.recursive();
 
-        match (
-            recursive,
-            cap.list_with_recursive,
-            cap.list_without_recursive,
-        ) {
-            // - If service can both list_with_recursive and 
list_without_recursive
-            // - If recursive is true while services can list_with_recursive
-            // - If recursive is false while services can 
list_without_recursive
-            (_, true, true) | (true, true, _) | (false, _, true) => {
+        match (recursive, cap.list_with_recursive) {
+            // - If service can both list_with_recursive
+            // - If recursive is false
+            (_, true) | (false, _) => {
                 let (rp, p) = self.inner.blocking_list(path, args)?;
                 Ok((rp, CompleteLister::AlreadyComplete(p)))
             }
-            // If services can't list_with_recursive nor 
list_without_recursive.
-            //
-            // It should be a service level bug.
-            (_, false, false) => 
Err(self.new_unsupported_error(Operation::List)),
             // If recursive is true but service can't list_with_recursive
-            (true, false, true) => {
+            (true, false) => {
                 let p = FlatLister::new(self.inner.clone(), path);
                 Ok((RpList::default(), CompleteLister::NeedFlat(p)))
             }
-            // If recursive is false but service can't list_without_recursive
-            (false, true, false) => {
-                let (_, p) = self.inner.blocking_list(path, 
args.with_recursive(true))?;
-                let p: HierarchyLister<<A as Accessor>::BlockingLister> =
-                    HierarchyLister::new(p, path);
-                Ok((RpList::default(), CompleteLister::NeedHierarchy(p)))
-            }
         }
     }
 }
@@ -738,7 +706,6 @@ where
 pub enum CompleteLister<A: Accessor, P> {
     AlreadyComplete(P),
     NeedFlat(FlatLister<Arc<A>, P>),
-    NeedHierarchy(HierarchyLister<P>),
 }
 
 #[async_trait]
@@ -753,7 +720,6 @@ where
         match self {
             AlreadyComplete(p) => p.poll_next(cx),
             NeedFlat(p) => p.poll_next(cx),
-            NeedHierarchy(p) => p.poll_next(cx),
         }
     }
 }
@@ -769,7 +735,6 @@ where
         match self {
             AlreadyComplete(p) => p.next(),
             NeedFlat(p) => p.next(),
-            NeedHierarchy(p) => p.next(),
         }
     }
 }
diff --git a/core/src/layers/immutable_index.rs 
b/core/src/layers/immutable_index.rs
index 4a57f4e19..993c7eba7 100644
--- a/core/src/layers/immutable_index.rs
+++ b/core/src/layers/immutable_index.rs
@@ -156,7 +156,6 @@ impl<A: Accessor> LayeredAccessor for 
ImmutableIndexAccessor<A> {
         let cap = meta.full_capability_mut();
         cap.list = true;
         cap.list_with_recursive = true;
-        cap.list_without_recursive = true;
 
         meta
     }
diff --git a/core/src/layers/retry.rs b/core/src/layers/retry.rs
index 3db6f793b..8b0a44bb8 100644
--- a/core/src/layers/retry.rs
+++ b/core/src/layers/retry.rs
@@ -1173,7 +1173,6 @@ mod tests {
             am.set_native_capability(Capability {
                 read: true,
                 list: true,
-                list_without_recursive: true,
                 list_with_recursive: true,
                 batch: true,
                 ..Default::default()
diff --git a/core/src/raw/adapters/kv/backend.rs 
b/core/src/raw/adapters/kv/backend.rs
index c4e68d8c7..a05b1da67 100644
--- a/core/src/raw/adapters/kv/backend.rs
+++ b/core/src/raw/adapters/kv/backend.rs
@@ -28,6 +28,7 @@ use futures::future::BoxFuture;
 use futures::FutureExt;
 
 use super::Adapter;
+use crate::raw::oio::HierarchyLister;
 use crate::raw::*;
 use crate::*;
 
@@ -69,8 +70,8 @@ impl<S: Adapter> Accessor for Backend<S> {
     type BlockingReader = oio::Cursor;
     type Writer = KvWriter<S>;
     type BlockingWriter = KvWriter<S>;
-    type Lister = KvLister;
-    type BlockingLister = KvLister;
+    type Lister = HierarchyLister<KvLister>;
+    type BlockingLister = HierarchyLister<KvLister>;
 
     fn info(&self) -> AccessorInfo {
         let mut am: AccessorInfo = self.kv.metadata().into();
@@ -89,14 +90,6 @@ impl<S: Adapter> Accessor for Backend<S> {
             cap.delete = true;
         }
 
-        if cap.read && cap.write {
-            cap.copy = true;
-        }
-
-        if cap.read && cap.write && cap.delete {
-            cap.rename = true;
-        }
-
         if cap.list {
             cap.list_with_recursive = true;
         }
@@ -189,83 +182,23 @@ impl<S: Adapter> Accessor for Backend<S> {
         Ok(RpDelete::default())
     }
 
-    async fn list(&self, path: &str, _: OpList) -> Result<(RpList, 
Self::Lister)> {
+    async fn list(&self, path: &str, args: OpList) -> Result<(RpList, 
Self::Lister)> {
         let p = build_abs_path(&self.root, path);
         let res = self.kv.scan(&p).await?;
         let lister = KvLister::new(&self.root, res);
+        let lister = HierarchyLister::new(lister, path, args.recursive());
 
         Ok((RpList::default(), lister))
     }
 
-    fn blocking_list(&self, path: &str, _: OpList) -> Result<(RpList, 
Self::BlockingLister)> {
+    fn blocking_list(&self, path: &str, args: OpList) -> Result<(RpList, 
Self::BlockingLister)> {
         let p = build_abs_path(&self.root, path);
         let res = self.kv.blocking_scan(&p)?;
         let lister = KvLister::new(&self.root, res);
+        let lister = HierarchyLister::new(lister, path, args.recursive());
 
         Ok((RpList::default(), lister))
     }
-
-    async fn rename(&self, from: &str, to: &str, _: OpRename) -> 
Result<RpRename> {
-        let from = build_abs_path(&self.root, from);
-        let to = build_abs_path(&self.root, to);
-
-        let bs = match self.kv.get(&from).await? {
-            // TODO: we can reuse the metadata in value to build content range.
-            Some(bs) => bs,
-            None => return Err(Error::new(ErrorKind::NotFound, "kv doesn't 
have this path")),
-        };
-
-        self.kv.set(&to, &bs).await?;
-
-        self.kv.delete(&from).await?;
-        Ok(RpRename::default())
-    }
-
-    fn blocking_rename(&self, from: &str, to: &str, _: OpRename) -> 
Result<RpRename> {
-        let from = build_abs_path(&self.root, from);
-        let to = build_abs_path(&self.root, to);
-
-        let bs = match self.kv.blocking_get(&from)? {
-            // TODO: we can reuse the metadata in value to build content range.
-            Some(bs) => bs,
-            None => return Err(Error::new(ErrorKind::NotFound, "kv doesn't 
have this path")),
-        };
-
-        self.kv.blocking_set(&to, &bs)?;
-
-        self.kv.blocking_delete(&from)?;
-        Ok(RpRename::default())
-    }
-
-    async fn copy(&self, from: &str, to: &str, _: OpCopy) -> Result<RpCopy> {
-        let from = build_abs_path(&self.root, from);
-        let to = build_abs_path(&self.root, to);
-
-        let bs = match self.kv.get(&from).await? {
-            // TODO: we can reuse the metadata in value to build content range.
-            Some(bs) => bs,
-            None => return Err(Error::new(ErrorKind::NotFound, "kv doesn't 
have this path")),
-        };
-
-        self.kv.set(&to, &bs).await?;
-
-        Ok(RpCopy::default())
-    }
-
-    fn blocking_copy(&self, from: &str, to: &str, _: OpCopy) -> Result<RpCopy> 
{
-        let from = build_abs_path(&self.root, from);
-        let to = build_abs_path(&self.root, to);
-
-        let bs = match self.kv.blocking_get(&from)? {
-            // TODO: we can reuse the metadata in value to build content range.
-            Some(bs) => bs,
-            None => return Err(Error::new(ErrorKind::NotFound, "kv doesn't 
have this path")),
-        };
-
-        self.kv.blocking_set(&to, &bs)?;
-
-        Ok(RpCopy::default())
-    }
 }
 
 impl<S> Backend<S>
diff --git a/core/src/raw/adapters/typed_kv/backend.rs 
b/core/src/raw/adapters/typed_kv/backend.rs
index 266d41724..b02c99ec5 100644
--- a/core/src/raw/adapters/typed_kv/backend.rs
+++ b/core/src/raw/adapters/typed_kv/backend.rs
@@ -28,6 +28,7 @@ use futures::FutureExt;
 
 use super::Adapter;
 use super::Value;
+use crate::raw::oio::HierarchyLister;
 use crate::raw::*;
 use crate::*;
 
@@ -63,8 +64,8 @@ impl<S: Adapter> Accessor for Backend<S> {
     type BlockingReader = oio::Cursor;
     type Writer = KvWriter<S>;
     type BlockingWriter = KvWriter<S>;
-    type Lister = KvLister;
-    type BlockingLister = KvLister;
+    type Lister = HierarchyLister<KvLister>;
+    type BlockingLister = HierarchyLister<KvLister>;
 
     fn info(&self) -> AccessorInfo {
         let kv_info = self.kv.info();
@@ -97,13 +98,6 @@ impl<S: Adapter> Accessor for Backend<S> {
             cap.list_with_recursive = true;
         }
 
-        if cap.read && cap.write {
-            cap.copy = true;
-        }
-
-        if cap.read && cap.write && cap.delete {
-            cap.rename = true;
-        }
         cap.blocking = true;
 
         am.set_native_capability(cap);
@@ -192,81 +186,23 @@ impl<S: Adapter> Accessor for Backend<S> {
         Ok(RpDelete::default())
     }
 
-    async fn list(&self, path: &str, _: OpList) -> Result<(RpList, 
Self::Lister)> {
+    async fn list(&self, path: &str, args: OpList) -> Result<(RpList, 
Self::Lister)> {
         let p = build_abs_path(&self.root, path);
         let res = self.kv.scan(&p).await?;
         let lister = KvLister::new(&self.root, res);
+        let lister = HierarchyLister::new(lister, path, args.recursive());
 
         Ok((RpList::default(), lister))
     }
 
-    fn blocking_list(&self, path: &str, _: OpList) -> Result<(RpList, 
Self::BlockingLister)> {
+    fn blocking_list(&self, path: &str, args: OpList) -> Result<(RpList, 
Self::BlockingLister)> {
         let p = build_abs_path(&self.root, path);
         let res = self.kv.blocking_scan(&p)?;
         let lister = KvLister::new(&self.root, res);
+        let lister = HierarchyLister::new(lister, path, args.recursive());
 
         Ok((RpList::default(), lister))
     }
-
-    async fn rename(&self, from: &str, to: &str, _: OpRename) -> 
Result<RpRename> {
-        let from = build_abs_path(&self.root, from);
-        let to = build_abs_path(&self.root, to);
-
-        let bs = match self.kv.get(&from).await? {
-            // TODO: we can reuse the metadata in value to build content range.
-            Some(bs) => bs,
-            None => return Err(Error::new(ErrorKind::NotFound, "kv doesn't 
have this path")),
-        };
-
-        self.kv.set(&to, bs).await?;
-        self.kv.delete(&from).await?;
-        Ok(RpRename::default())
-    }
-
-    fn blocking_rename(&self, from: &str, to: &str, _: OpRename) -> 
Result<RpRename> {
-        let from = build_abs_path(&self.root, from);
-        let to = build_abs_path(&self.root, to);
-
-        let bs = match self.kv.blocking_get(&from)? {
-            // TODO: we can reuse the metadata in value to build content range.
-            Some(bs) => bs,
-            None => return Err(Error::new(ErrorKind::NotFound, "kv doesn't 
have this path")),
-        };
-
-        self.kv.blocking_set(&to, bs)?;
-        self.kv.blocking_delete(&from)?;
-        Ok(RpRename::default())
-    }
-
-    async fn copy(&self, from: &str, to: &str, _: OpCopy) -> Result<RpCopy> {
-        let from = build_abs_path(&self.root, from);
-        let to = build_abs_path(&self.root, to);
-
-        let bs = match self.kv.get(&from).await? {
-            // TODO: we can reuse the metadata in value to build content range.
-            Some(bs) => bs,
-            None => return Err(Error::new(ErrorKind::NotFound, "kv doesn't 
have this path")),
-        };
-
-        self.kv.set(&to, bs).await?;
-
-        Ok(RpCopy::default())
-    }
-
-    fn blocking_copy(&self, from: &str, to: &str, _: OpCopy) -> Result<RpCopy> 
{
-        let from = build_abs_path(&self.root, from);
-        let to = build_abs_path(&self.root, to);
-
-        let bs = match self.kv.blocking_get(&from)? {
-            // TODO: we can reuse the metadata in value to build content range.
-            Some(bs) => bs,
-            None => return Err(Error::new(ErrorKind::NotFound, "kv doesn't 
have this path")),
-        };
-
-        self.kv.blocking_set(&to, bs)?;
-
-        Ok(RpCopy::default())
-    }
 }
 
 impl<S> Backend<S>
diff --git a/core/src/raw/oio/list/flat_list.rs 
b/core/src/raw/oio/list/flat_list.rs
index e6363bf80..9e8e2a5ac 100644
--- a/core/src/raw/oio/list/flat_list.rs
+++ b/core/src/raw/oio/list/flat_list.rs
@@ -84,15 +84,6 @@ where
 {
     /// Create a new flat lister
     pub fn new(acc: A, path: &str) -> FlatLister<A, L> {
-        #[cfg(debug_assertions)]
-        {
-            let meta = acc.info();
-            debug_assert!(
-                meta.full_capability().list_without_recursive,
-                "service doesn't support list hierarchy, it must be a bug"
-            );
-        }
-
         FlatLister {
             acc: Some(acc),
             root: path.to_string(),
@@ -254,7 +245,6 @@ mod tests {
         fn info(&self) -> AccessorInfo {
             let mut am = AccessorInfo::default();
             am.full_capability_mut().list = true;
-            am.full_capability_mut().list_without_recursive = true;
 
             am
         }
diff --git a/core/src/raw/oio/list/hierarchy_list.rs 
b/core/src/raw/oio/list/hierarchy_list.rs
index 3c67abc11..259e45532 100644
--- a/core/src/raw/oio/list/hierarchy_list.rs
+++ b/core/src/raw/oio/list/hierarchy_list.rs
@@ -37,11 +37,12 @@ pub struct HierarchyLister<P> {
     lister: P,
     path: String,
     visited: HashSet<String>,
+    recursive: bool,
 }
 
 impl<P> HierarchyLister<P> {
     /// Create a new hierarchy lister
-    pub fn new(lister: P, path: &str) -> HierarchyLister<P> {
+    pub fn new(lister: P, path: &str, recursive: bool) -> HierarchyLister<P> {
         let path = if path == "/" {
             "".to_string()
         } else {
@@ -52,6 +53,7 @@ impl<P> HierarchyLister<P> {
             lister,
             path,
             visited: HashSet::default(),
+            recursive,
         }
     }
 
@@ -75,6 +77,11 @@ impl<P> HierarchyLister<P> {
             return false;
         }
 
+        // Don't return already visited path.
+        if self.visited.contains(e.path()) {
+            return false;
+        }
+
         let prefix_len = self.path.len();
 
         let idx = if let Some(idx) = e.path()[prefix_len..].find('/') {
@@ -125,6 +132,9 @@ impl<P: oio::List> oio::List for HierarchyLister<P> {
                 None => return Poll::Ready(Ok(None)),
             };
 
+            if self.recursive {
+                return Poll::Ready(Ok(Some(entry)));
+            }
             if self.keep_entry(&mut entry) {
                 return Poll::Ready(Ok(Some(entry)));
             }
@@ -140,6 +150,10 @@ impl<P: oio::BlockingList> oio::BlockingList for 
HierarchyLister<P> {
                 None => return Ok(None),
             };
 
+            if self.recursive {
+                return Ok(Some(entry));
+            }
+
             if self.keep_entry(&mut entry) {
                 return Ok(Some(entry));
             }
@@ -191,7 +205,7 @@ mod tests {
         let lister = MockLister::new(vec![
             "x/x/", "x/y/", "y/", "x/x/x", "y/y", "xy/", "z", "y/a",
         ]);
-        let mut lister = HierarchyLister::new(lister, "");
+        let mut lister = HierarchyLister::new(lister, "", false);
 
         let mut entries = Vec::default();
 
diff --git a/core/src/services/alluxio/backend.rs 
b/core/src/services/alluxio/backend.rs
index 8cbf902d4..078a871da 100644
--- a/core/src/services/alluxio/backend.rs
+++ b/core/src/services/alluxio/backend.rs
@@ -204,7 +204,6 @@ impl Accessor for AlluxioBackend {
                 delete: true,
 
                 list: true,
-                list_without_recursive: true,
 
                 ..Default::default()
             });
diff --git a/core/src/services/azblob/backend.rs 
b/core/src/services/azblob/backend.rs
index 7aca06dd2..bf77111e0 100644
--- a/core/src/services/azblob/backend.rs
+++ b/core/src/services/azblob/backend.rs
@@ -575,7 +575,6 @@ impl Accessor for AzblobBackend {
                 copy: true,
 
                 list: true,
-                list_without_recursive: true,
                 list_with_recursive: true,
 
                 presign: self.has_sas_token,
diff --git a/core/src/services/azdls/backend.rs 
b/core/src/services/azdls/backend.rs
index a494ece63..164a2d7f7 100644
--- a/core/src/services/azdls/backend.rs
+++ b/core/src/services/azdls/backend.rs
@@ -255,7 +255,6 @@ impl Accessor for AzdlsBackend {
                 rename: true,
 
                 list: true,
-                list_without_recursive: true,
 
                 ..Default::default()
             });
diff --git a/core/src/services/azfile/backend.rs 
b/core/src/services/azfile/backend.rs
index ae3d8807b..f8b7f9418 100644
--- a/core/src/services/azfile/backend.rs
+++ b/core/src/services/azfile/backend.rs
@@ -270,7 +270,6 @@ impl Accessor for AzfileBackend {
                 rename: true,
 
                 list: true,
-                list_without_recursive: true,
 
                 ..Default::default()
             });
diff --git a/core/src/services/b2/backend.rs b/core/src/services/b2/backend.rs
index b689acb0b..4c6c8660d 100644
--- a/core/src/services/b2/backend.rs
+++ b/core/src/services/b2/backend.rs
@@ -310,7 +310,6 @@ impl Accessor for B2Backend {
                 list_with_limit: true,
                 list_with_start_after: true,
                 list_with_recursive: true,
-                list_without_recursive: true,
 
                 presign: true,
                 presign_read: true,
diff --git a/core/src/services/cos/backend.rs b/core/src/services/cos/backend.rs
index 9583a8b5b..1abc9c5a6 100644
--- a/core/src/services/cos/backend.rs
+++ b/core/src/services/cos/backend.rs
@@ -288,7 +288,6 @@ impl Accessor for CosBackend {
                 copy: true,
 
                 list: true,
-                list_without_recursive: true,
                 list_with_recursive: true,
 
                 presign: true,
diff --git a/core/src/services/dashmap/backend.rs 
b/core/src/services/dashmap/backend.rs
index f64dd2865..b85b528e2 100644
--- a/core/src/services/dashmap/backend.rs
+++ b/core/src/services/dashmap/backend.rs
@@ -16,7 +16,7 @@
 // under the License.
 
 use std::collections::HashMap;
-use std::fmt::Debug;
+use std::fmt::{Debug, Formatter};
 
 use async_trait::async_trait;
 use dashmap::DashMap;
@@ -62,11 +62,19 @@ impl Builder for DashmapBuilder {
 /// Backend is used to serve `Accessor` support in dashmap.
 pub type DashmapBackend = typed_kv::Backend<Adapter>;
 
-#[derive(Debug, Clone)]
+#[derive(Clone)]
 pub struct Adapter {
     inner: DashMap<String, typed_kv::Value>,
 }
 
+impl Debug for Adapter {
+    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
+        f.debug_struct("DashmapAdapter")
+            .field("size", &self.inner.len())
+            .finish_non_exhaustive()
+    }
+}
+
 #[async_trait]
 impl typed_kv::Adapter for Adapter {
     fn info(&self) -> typed_kv::Info {
diff --git a/core/src/services/dbfs/backend.rs 
b/core/src/services/dbfs/backend.rs
index 4d85c3aad..6a5fcb602 100644
--- a/core/src/services/dbfs/backend.rs
+++ b/core/src/services/dbfs/backend.rs
@@ -177,7 +177,6 @@ impl Accessor for DbfsBackend {
                 rename: true,
 
                 list: true,
-                list_without_recursive: true,
 
                 ..Default::default()
             });
diff --git a/core/src/services/fs/backend.rs b/core/src/services/fs/backend.rs
index 892a0f79b..e2205e03c 100644
--- a/core/src/services/fs/backend.rs
+++ b/core/src/services/fs/backend.rs
@@ -265,7 +265,6 @@ impl Accessor for FsBackend {
                 delete: true,
 
                 list: true,
-                list_without_recursive: true,
 
                 copy: true,
                 rename: true,
diff --git a/core/src/services/ftp/backend.rs b/core/src/services/ftp/backend.rs
index 5b450875f..b0c2a4544 100644
--- a/core/src/services/ftp/backend.rs
+++ b/core/src/services/ftp/backend.rs
@@ -306,7 +306,6 @@ impl Accessor for FtpBackend {
                 create_dir: true,
 
                 list: true,
-                list_without_recursive: true,
 
                 ..Default::default()
             });
diff --git a/core/src/services/gcs/backend.rs b/core/src/services/gcs/backend.rs
index 10259d939..8fe1a75c2 100644
--- a/core/src/services/gcs/backend.rs
+++ b/core/src/services/gcs/backend.rs
@@ -348,7 +348,6 @@ impl Accessor for GcsBackend {
                 list: true,
                 list_with_limit: true,
                 list_with_start_after: true,
-                list_without_recursive: true,
                 list_with_recursive: true,
 
                 batch: true,
diff --git a/core/src/services/gdrive/backend.rs 
b/core/src/services/gdrive/backend.rs
index be8e4b88a..c2e6a874e 100644
--- a/core/src/services/gdrive/backend.rs
+++ b/core/src/services/gdrive/backend.rs
@@ -58,7 +58,6 @@ impl Accessor for GdriveBackend {
                 read: true,
 
                 list: true,
-                list_without_recursive: true,
 
                 write: true,
 
diff --git a/core/src/services/hdfs/backend.rs 
b/core/src/services/hdfs/backend.rs
index ea857d4f6..bdff53de9 100644
--- a/core/src/services/hdfs/backend.rs
+++ b/core/src/services/hdfs/backend.rs
@@ -196,7 +196,6 @@ impl Accessor for HdfsBackend {
                 delete: true,
 
                 list: true,
-                list_without_recursive: true,
 
                 rename: true,
                 blocking: true,
diff --git a/core/src/services/huggingface/backend.rs 
b/core/src/services/huggingface/backend.rs
index 37c0b5064..b9d98867e 100644
--- a/core/src/services/huggingface/backend.rs
+++ b/core/src/services/huggingface/backend.rs
@@ -261,7 +261,6 @@ impl Accessor for HuggingfaceBackend {
                 read_with_range: true,
 
                 list: true,
-                list_without_recursive: true,
                 list_with_recursive: true,
 
                 ..Default::default()
diff --git a/core/src/services/ipfs/backend.rs 
b/core/src/services/ipfs/backend.rs
index 915e2b798..081a56e73 100644
--- a/core/src/services/ipfs/backend.rs
+++ b/core/src/services/ipfs/backend.rs
@@ -180,7 +180,6 @@ impl Accessor for IpfsBackend {
                 read_with_range: true,
 
                 list: true,
-                list_without_recursive: true,
 
                 ..Default::default()
             });
diff --git a/core/src/services/ipmfs/backend.rs 
b/core/src/services/ipmfs/backend.rs
index 7a9da9b4e..0433028f0 100644
--- a/core/src/services/ipmfs/backend.rs
+++ b/core/src/services/ipmfs/backend.rs
@@ -84,7 +84,6 @@ impl Accessor for IpmfsBackend {
                 delete: true,
 
                 list: true,
-                list_without_recursive: true,
 
                 ..Default::default()
             });
diff --git a/core/src/services/obs/backend.rs b/core/src/services/obs/backend.rs
index e753e800d..d72cca73c 100644
--- a/core/src/services/obs/backend.rs
+++ b/core/src/services/obs/backend.rs
@@ -294,7 +294,6 @@ impl Accessor for ObsBackend {
                 copy: true,
 
                 list: true,
-                list_without_recursive: true,
                 list_with_recursive: true,
 
                 presign: true,
diff --git a/core/src/services/onedrive/backend.rs 
b/core/src/services/onedrive/backend.rs
index ba4e5b8d3..db7ed22c3 100644
--- a/core/src/services/onedrive/backend.rs
+++ b/core/src/services/onedrive/backend.rs
@@ -80,7 +80,6 @@ impl Accessor for OnedriveBackend {
                 delete: true,
                 create_dir: true,
                 list: true,
-                list_without_recursive: true,
                 ..Default::default()
             });
 
diff --git a/core/src/services/oss/backend.rs b/core/src/services/oss/backend.rs
index 15fc23f6b..42a8b9d19 100644
--- a/core/src/services/oss/backend.rs
+++ b/core/src/services/oss/backend.rs
@@ -425,7 +425,6 @@ impl Accessor for OssBackend {
                 list: true,
                 list_with_limit: true,
                 list_with_start_after: true,
-                list_without_recursive: true,
                 list_with_recursive: true,
 
                 presign: true,
diff --git a/core/src/services/s3/backend.rs b/core/src/services/s3/backend.rs
index a6317a6cd..4d9522620 100644
--- a/core/src/services/s3/backend.rs
+++ b/core/src/services/s3/backend.rs
@@ -1016,7 +1016,6 @@ impl Accessor for S3Backend {
                 list_with_limit: true,
                 list_with_start_after: true,
                 list_with_recursive: true,
-                list_without_recursive: true,
 
                 presign: true,
                 presign_stat: true,
diff --git a/core/src/services/sftp/backend.rs 
b/core/src/services/sftp/backend.rs
index ec9e2de4f..572bb4dd1 100644
--- a/core/src/services/sftp/backend.rs
+++ b/core/src/services/sftp/backend.rs
@@ -267,7 +267,6 @@ impl Accessor for SftpBackend {
 
                 list: true,
                 list_with_limit: true,
-                list_without_recursive: true,
 
                 copy: self.copyable,
                 rename: true,
diff --git a/core/src/services/swift/backend.rs 
b/core/src/services/swift/backend.rs
index 4e9e60540..6ae2a3b6a 100644
--- a/core/src/services/swift/backend.rs
+++ b/core/src/services/swift/backend.rs
@@ -237,7 +237,6 @@ impl Accessor for SwiftBackend {
                 delete: true,
 
                 list: true,
-                list_without_recursive: true,
                 list_with_recursive: true,
 
                 ..Default::default()
diff --git a/core/src/services/webdav/backend.rs 
b/core/src/services/webdav/backend.rs
index 1361a11fd..b751cf026 100644
--- a/core/src/services/webdav/backend.rs
+++ b/core/src/services/webdav/backend.rs
@@ -248,7 +248,6 @@ impl Accessor for WebdavBackend {
                 rename: true,
 
                 list: true,
-                list_without_recursive: true,
 
                 ..Default::default()
             });
diff --git a/core/src/services/webhdfs/backend.rs 
b/core/src/services/webhdfs/backend.rs
index 9fa9bd150..8a6a9f297 100644
--- a/core/src/services/webhdfs/backend.rs
+++ b/core/src/services/webhdfs/backend.rs
@@ -417,7 +417,6 @@ impl Accessor for WebhdfsBackend {
                 delete: true,
 
                 list: true,
-                list_without_recursive: true,
 
                 ..Default::default()
             });
diff --git a/core/src/types/capability.rs b/core/src/types/capability.rs
index c6f4fc52c..0847afa5e 100644
--- a/core/src/types/capability.rs
+++ b/core/src/types/capability.rs
@@ -132,8 +132,6 @@ pub struct Capability {
     pub list_with_start_after: bool,
     /// If backend supports list with recursive.
     pub list_with_recursive: bool,
-    /// If backend supports list without recursive.
-    pub list_without_recursive: bool,
 
     /// If operator supports presign.
     pub presign: bool,
diff --git a/core/tests/behavior/list.rs b/core/tests/behavior/list.rs
index 64911f8fa..17f4a695a 100644
--- a/core/tests/behavior/list.rs
+++ b/core/tests/behavior/list.rs
@@ -261,7 +261,8 @@ pub async fn test_list_sub_dir(op: Operator) -> Result<()> {
 
 /// List dir should also to list nested dir.
 pub async fn test_list_nested_dir(op: Operator) -> Result<()> {
-    let dir = format!("{}/{}/", uuid::Uuid::new_v4(), uuid::Uuid::new_v4());
+    let parent = format!("{}/", uuid::Uuid::new_v4());
+    let dir = format!("{parent}{}/", uuid::Uuid::new_v4());
 
     let file_name = uuid::Uuid::new_v4().to_string();
     let file_path = format!("{dir}{file_name}");
@@ -274,6 +275,11 @@ pub async fn test_list_nested_dir(op: Operator) -> 
Result<()> {
         .expect("creat must succeed");
     op.create_dir(&dir_path).await.expect("creat must succeed");
 
+    let obs = op.list(&parent).await?;
+    assert_eq!(obs.len(), 1, "parent should only got 1 entry");
+    assert_eq!(obs[0].path(), dir);
+    assert_eq!(obs[0].metadata().mode(), EntryMode::DIR);
+
     let mut obs = op.lister(&dir).await?;
     let mut objects = HashMap::new();
 


Reply via email to