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/opendal.git


The following commit(s) were added to refs/heads/main by this push:
     new 06235da7f refactor: Deprecate remove_all API in favor of RFC-3911: 
Deleter API (#6785)
06235da7f is described below

commit 06235da7f01062b21515144018208a519cca8d28
Author: KUAN-HAO HUANG <[email protected]>
AuthorDate: Tue Dec 16 21:29:59 2025 +0800

    refactor: Deprecate remove_all API in favor of RFC-3911: Deleter API (#6785)
    
    * try 3922
    
    * decrease explanation
    
    * allow
    
    * fix fmt error
    
    * update
    
    * fix bindings
    
    * fix errors
    
    * fix format error
    
    * allow
    
    * update
    
    * fix import
    
    * fmt error
    
    * fix error from behavior test local_fs
    
    * remove redundant part
    
    * modify ci error toml-format
    
    * remove redundant part
    
    * Format code
    
    ---------
    
    Co-authored-by: rich <[email protected]>
    Co-authored-by: Xuanwo <[email protected]>
---
 bindings/dart/rust/src/frb_generated.rs  |  6 ++---
 bindings/haskell/src/lib.rs              | 15 ++++++++---
 bindings/java/src/async_operator.rs      |  3 ++-
 bindings/java/src/operator.rs            |  9 ++++++-
 bindings/nodejs/src/lib.rs               | 12 +++++++--
 bindings/ocaml/src/operator/mod.rs       |  9 ++++++-
 bindings/python/src/operator.rs          | 16 ++++++++++--
 bindings/ruby/src/operator.rs            |  9 ++++++-
 core/core/src/blocking/operator.rs       | 37 +++++++++++++++++++++++++--
 core/core/src/types/operator/operator.rs | 43 ++++++++++++++++----------------
 core/tests/behavior/async_delete.rs      |  2 +-
 core/tests/behavior/async_list.rs        | 12 +++++----
 integrations/unftp-sbe/src/lib.rs        |  3 ++-
 13 files changed, 132 insertions(+), 44 deletions(-)

diff --git a/bindings/dart/rust/src/frb_generated.rs 
b/bindings/dart/rust/src/frb_generated.rs
index 04c6604be..f867fb21d 100644
--- a/bindings/dart/rust/src/frb_generated.rs
+++ b/bindings/dart/rust/src/frb_generated.rs
@@ -27,7 +27,7 @@
 
 use crate::api::opendal_api::*;
 use flutter_rust_bridge::for_generated::byteorder::{NativeEndian, 
ReadBytesExt, WriteBytesExt};
-use flutter_rust_bridge::for_generated::{Lifetimeable, Lockable, 
transform_result_dco};
+use flutter_rust_bridge::for_generated::{transform_result_dco, Lifetimeable, 
Lockable};
 use flutter_rust_bridge::{Handler, IntoIntoDart};
 
 // Section: boilerplate
@@ -1491,7 +1491,7 @@ mod io {
     use flutter_rust_bridge::for_generated::byteorder::{
         NativeEndian, ReadBytesExt, WriteBytesExt,
     };
-    use flutter_rust_bridge::for_generated::{Lifetimeable, Lockable, 
transform_result_dco};
+    use flutter_rust_bridge::for_generated::{transform_result_dco, 
Lifetimeable, Lockable};
     use flutter_rust_bridge::{Handler, IntoIntoDart};
 
     // Section: boilerplate
@@ -1544,7 +1544,7 @@ mod web {
     };
     use flutter_rust_bridge::for_generated::wasm_bindgen;
     use flutter_rust_bridge::for_generated::wasm_bindgen::prelude::*;
-    use flutter_rust_bridge::for_generated::{Lifetimeable, Lockable, 
transform_result_dco};
+    use flutter_rust_bridge::for_generated::{transform_result_dco, 
Lifetimeable, Lockable};
     use flutter_rust_bridge::{Handler, IntoIntoDart};
 
     // Section: boilerplate
diff --git a/bindings/haskell/src/lib.rs b/bindings/haskell/src/lib.rs
index a142fe603..f196002a0 100644
--- a/bindings/haskell/src/lib.rs
+++ b/bindings/haskell/src/lib.rs
@@ -706,9 +706,18 @@ pub unsafe extern "C" fn blocking_remove_all(
             }
         };
 
-        let res = match op.remove_all(path_str) {
-            Ok(()) => FFIResult::ok(()),
-            Err(e) => FFIResult::err_with_source("Failed to remove all", e),
+        let res = {
+            use od::options::DeleteOptions;
+            match op.delete_options(
+                path_str,
+                DeleteOptions {
+                    recursive: true,
+                    ..Default::default()
+                },
+            ) {
+                Ok(()) => FFIResult::ok(()),
+                Err(e) => FFIResult::err_with_source("Failed to remove all", 
e),
+            }
         };
 
         *result = res;
diff --git a/bindings/java/src/async_operator.rs 
b/bindings/java/src/async_operator.rs
index 0494e7895..8e3852439 100644
--- a/bindings/java/src/async_operator.rs
+++ b/bindings/java/src/async_operator.rs
@@ -469,7 +469,8 @@ fn intern_remove_all(
 
     executor_or_default(env, executor)?.spawn(async move {
         let result = op
-            .remove_all(&path)
+            .delete_with(&path)
+            .recursive(true)
             .await
             .map(|_| JValueOwned::Void)
             .map_err(Into::into);
diff --git a/bindings/java/src/operator.rs b/bindings/java/src/operator.rs
index 146177512..08922dfdf 100644
--- a/bindings/java/src/operator.rs
+++ b/bindings/java/src/operator.rs
@@ -283,9 +283,16 @@ pub unsafe extern "system" fn 
Java_org_apache_opendal_Operator_removeAll(
 }
 
 fn intern_remove_all(env: &mut JNIEnv, op: &mut blocking::Operator, path: 
JString) -> Result<()> {
+    use opendal::options::DeleteOptions;
     let path = jstring_to_string(env, &path)?;
 
-    Ok(op.remove_all(&path)?)
+    Ok(op.delete_options(
+        &path,
+        DeleteOptions {
+            recursive: true,
+            ..Default::default()
+        },
+    )?)
 }
 
 /// # Safety
diff --git a/bindings/nodejs/src/lib.rs b/bindings/nodejs/src/lib.rs
index 510b425eb..e56380e0a 100644
--- a/bindings/nodejs/src/lib.rs
+++ b/bindings/nodejs/src/lib.rs
@@ -516,7 +516,8 @@ impl Operator {
     #[napi]
     pub async fn remove_all(&self, path: String) -> Result<()> {
         self.async_op
-            .remove_all(&path)
+            .delete_with(&path)
+            .recursive(true)
             .await
             .map_err(format_napi_error)
     }
@@ -532,8 +533,15 @@ impl Operator {
     /// ```
     #[napi]
     pub fn remove_all_sync(&self, path: String) -> Result<()> {
+        use opendal::options::DeleteOptions;
         self.blocking_op
-            .remove_all(&path)
+            .delete_options(
+                &path,
+                DeleteOptions {
+                    recursive: true,
+                    ..Default::default()
+                },
+            )
             .map_err(format_napi_error)
     }
 
diff --git a/bindings/ocaml/src/operator/mod.rs 
b/bindings/ocaml/src/operator/mod.rs
index 420709966..ded6da0d2 100644
--- a/bindings/ocaml/src/operator/mod.rs
+++ b/bindings/ocaml/src/operator/mod.rs
@@ -148,7 +148,14 @@ pub fn blocking_remove(operator: &mut Operator, path: 
Vec<String>) -> Result<(),
 #[ocaml::func]
 #[ocaml::sig("operator -> string -> (unit, string) Result.t ")]
 pub fn blocking_remove_all(operator: &mut Operator, path: String) -> 
Result<(), String> {
-    map_res_error(operator.0.remove_all(path.as_str()))
+    use opendal::options::DeleteOptions;
+    map_res_error(operator.0.delete_options(
+        path.as_str(),
+        DeleteOptions {
+            recursive: true,
+            ..Default::default()
+        },
+    ))
 }
 
 #[ocaml::func]
diff --git a/bindings/python/src/operator.rs b/bindings/python/src/operator.rs
index 90015d41b..9f85f0ad3 100644
--- a/bindings/python/src/operator.rs
+++ b/bindings/python/src/operator.rs
@@ -484,8 +484,17 @@ impl Operator {
     /// path : str
     ///     The path to remove.
     pub fn remove_all(&self, path: PathBuf) -> PyResult<()> {
+        use ocore::options::DeleteOptions;
         let path = path.to_string_lossy().to_string();
-        self.core.remove_all(&path).map_err(format_pyerr)
+        self.core
+            .delete_options(
+                &path,
+                DeleteOptions {
+                    recursive: true,
+                    ..Default::default()
+                },
+            )
+            .map_err(format_pyerr)
     }
 
     /// Create a directory at the given path.
@@ -1219,7 +1228,10 @@ impl AsyncOperator {
         let this = self.core.clone();
         let path = path.to_string_lossy().to_string();
         future_into_py(py, async move {
-            this.remove_all(&path).await.map_err(format_pyerr)
+            this.delete_with(&path)
+                .recursive(true)
+                .await
+                .map_err(format_pyerr)
         })
     }
 
diff --git a/bindings/ruby/src/operator.rs b/bindings/ruby/src/operator.rs
index bb92a7b4b..c9ae692c9 100644
--- a/bindings/ruby/src/operator.rs
+++ b/bindings/ruby/src/operator.rs
@@ -200,9 +200,16 @@ impl Operator {
     /// @param path [String]
     /// @return [nil]
     fn remove_all(ruby: &Ruby, rb_self: &Self, path: String) -> Result<(), 
Error> {
+        use ocore::options::DeleteOptions;
         rb_self
             .blocking_op
-            .remove_all(&path)
+            .delete_options(
+                &path,
+                DeleteOptions {
+                    recursive: true,
+                    ..Default::default()
+                },
+            )
             .map_err(|err| Error::new(ruby.exception_runtime_error(), 
err.to_string()))
     }
 
diff --git a/core/core/src/blocking/operator.rs 
b/core/core/src/blocking/operator.rs
index 0f6fa89cb..740fec949 100644
--- a/core/core/src/blocking/operator.rs
+++ b/core/core/src/blocking/operator.rs
@@ -555,9 +555,31 @@ impl Operator {
 
     /// Remove the path and all nested dirs and files recursively.
     ///
+    /// # Deprecated
+    ///
+    /// This method is deprecated since v0.55.0. Use 
[`blocking::Operator::delete_options`] with
+    /// `recursive: true` instead.
+    ///
+    /// ## Migration Example
+    ///
+    /// Instead of:
+    /// ```ignore
+    /// op.remove_all("path/to/dir")?;
+    /// ```
+    ///
+    /// Use:
+    /// ```ignore
+    /// use opendal_core::options::DeleteOptions;
+    /// op.delete_options("path/to/dir", DeleteOptions {
+    ///     recursive: true,
+    ///     ..Default::default()
+    /// })?;
+    /// ```
+    ///
     /// # Notes
     ///
-    /// We don't support batch delete now.
+    /// If underlying services support delete in batch, we will use batch
+    /// delete instead.
     ///
     /// # Examples
     ///
@@ -571,8 +593,19 @@ impl Operator {
     /// # Ok(())
     /// # }
     /// ```
+    #[deprecated(
+        since = "0.55.0",
+        note = "Use `delete_options` with `recursive: true` instead"
+    )]
+    #[allow(deprecated)]
     pub fn remove_all(&self, path: &str) -> Result<()> {
-        self.handle.block_on(self.op.remove_all(path))
+        self.delete_options(
+            path,
+            options::DeleteOptions {
+                recursive: true,
+                ..Default::default()
+            },
+        )
     }
 
     /// List entries whose paths start with the given prefix `path`.
diff --git a/core/core/src/types/operator/operator.rs 
b/core/core/src/types/operator/operator.rs
index 15d6df711..caf895e57 100644
--- a/core/core/src/types/operator/operator.rs
+++ b/core/core/src/types/operator/operator.rs
@@ -1375,6 +1375,23 @@ impl Operator {
 
     /// Remove the path and all nested dirs and files recursively.
     ///
+    /// # Deprecated
+    ///
+    /// This method is deprecated since v0.55.0. Use [`Operator::delete_with`] 
with
+    /// `recursive(true)` instead.
+    ///
+    /// ## Migration Example
+    ///
+    /// Instead of:
+    /// ```ignore
+    /// op.remove_all("path/to/dir").await?;
+    /// ```
+    ///
+    /// Use:
+    /// ```ignore
+    /// op.delete_with("path/to/dir").recursive(true).await?;
+    /// ```
+    ///
     /// # Notes
     ///
     /// If underlying services support delete in batch, we will use batch
@@ -1392,28 +1409,12 @@ impl Operator {
     /// # Ok(())
     /// # }
     /// ```
+    #[deprecated(
+        since = "0.55.0",
+        note = "Use `delete_with` with `recursive(true)` instead"
+    )]
     pub async fn remove_all(&self, path: &str) -> Result<()> {
-        match self.stat(path).await {
-            // If object exists.
-            Ok(metadata) => {
-                // If the object is a file, we can delete it.
-                if metadata.mode() != EntryMode::DIR {
-                    self.delete(path).await?;
-                    // There may still be objects prefixed with the path in 
some backend, so we can't return here.
-                }
-            }
-
-            // If dir not found, it may be a prefix in object store like S3,
-            // and we still need to delete objects under the prefix.
-            Err(e) if e.kind() == ErrorKind::NotFound => {}
-
-            // Pass on any other error.
-            Err(e) => return Err(e),
-        };
-
-        let lister = self.lister_with(path).recursive(true).await?;
-        self.delete_try_stream(lister).await?;
-        Ok(())
+        self.delete_with(path).recursive(true).await
     }
 
     /// List entries whose paths start with the given prefix `path`.
diff --git a/core/tests/behavior/async_delete.rs 
b/core/tests/behavior/async_delete.rs
index 625496075..b1584badd 100644
--- a/core/tests/behavior/async_delete.rs
+++ b/core/tests/behavior/async_delete.rs
@@ -179,7 +179,7 @@ async fn test_blocking_remove_all_with_objects(
         op.write(&path, content).await.expect("write must succeed");
     }
 
-    op.remove_all(&parent).await?;
+    op.delete_with(&parent).recursive(true).await?;
 
     let found = op
         .lister_with(&format!("{parent}/"))
diff --git a/core/tests/behavior/async_list.rs 
b/core/tests/behavior/async_list.rs
index 8aecb7d92..2be5f0d0d 100644
--- a/core/tests/behavior/async_list.rs
+++ b/core/tests/behavior/async_list.rs
@@ -140,7 +140,7 @@ pub async fn test_list_rich_dir(op: Operator) -> Result<()> 
{
 
     assert_eq!(actual, expected);
 
-    op.remove_all(parent).await?;
+    op.delete_with(parent).recursive(true).await?;
     Ok(())
 }
 
@@ -399,7 +399,7 @@ pub async fn test_list_with_start_after(op: Operator) -> 
Result<()> {
 
     assert_eq!(expected, actual);
 
-    op.remove_all(dir).await?;
+    op.delete_with(dir).recursive(true).await?;
 
     Ok(())
 }
@@ -556,7 +556,9 @@ pub async fn test_remove_all(op: Operator) -> Result<()> {
         }
     }
 
-    op.remove_all(&format!("{parent}/x/")).await?;
+    op.delete_with(&format!("{parent}/x/"))
+        .recursive(true)
+        .await?;
 
     for path in expected.iter() {
         if path.ends_with('/') {
@@ -688,7 +690,7 @@ pub async fn test_list_with_versions_and_limit(op: 
Operator) -> Result<()> {
 
     assert_eq!(actual, expected);
 
-    op.remove_all(parent).await?;
+    op.delete_with(parent).recursive(true).await?;
     Ok(())
 }
 
@@ -735,7 +737,7 @@ pub async fn test_list_with_versions_and_start_after(op: 
Operator) -> Result<()>
     actual.sort_unstable();
     assert_eq!(expected, actual);
 
-    op.remove_all(dir).await?;
+    op.delete_with(dir).recursive(true).await?;
 
     Ok(())
 }
diff --git a/integrations/unftp-sbe/src/lib.rs 
b/integrations/unftp-sbe/src/lib.rs
index ba75050a5..80c443e55 100644
--- a/integrations/unftp-sbe/src/lib.rs
+++ b/integrations/unftp-sbe/src/lib.rs
@@ -263,7 +263,8 @@ impl<User: UserDetail> StorageBackend<User> for 
OpendalStorage {
 
     async fn rmd<P: AsRef<Path> + Send + Debug>(&self, _: &User, path: P) -> 
storage::Result<()> {
         self.op
-            .remove_all(convert_path(path.as_ref())?)
+            .delete_with(convert_path(path.as_ref())?)
+            .recursive(true)
             .await
             .map_err(convert_err)
     }

Reply via email to