You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@opendal.apache.org by xu...@apache.org on 2025/11/27 11:57:05 UTC
(opendal) branch xuanwo/fs-recursive-delete created (now a26cc9e32)
This is an automated email from the ASF dual-hosted git repository.
xuanwo pushed a change to branch xuanwo/fs-recursive-delete
in repository https://gitbox.apache.org/repos/asf/opendal.git
at a26cc9e32 feat: Add delete with recursive support for fs
This branch includes the following new commits:
new a26cc9e32 feat: Add delete with recursive support for fs
The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails. The revisions
listed as "add" were already present in the repository and have only
been added to this reference.
(opendal) 01/01: feat: Add delete with recursive support for fs
Posted by xu...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
xuanwo pushed a commit to branch xuanwo/fs-recursive-delete
in repository https://gitbox.apache.org/repos/asf/opendal.git
commit a26cc9e327f1fc5bb9f15a275e287c0b352c5a5c
Author: Xuanwo <gi...@xuanwo.io>
AuthorDate: Thu Nov 27 19:56:39 2025 +0800
feat: Add delete with recursive support for fs
Signed-off-by: Xuanwo <gi...@xuanwo.io>
---
core/src/layers/simulate.rs | 18 +++++++++++++-----
core/src/services/fs/backend.rs | 1 +
core/src/services/fs/deleter.rs | 12 ++++++++++--
core/tests/behavior/async_delete.rs | 35 +++++++++++++++++++++++++++++++++++
4 files changed, 59 insertions(+), 7 deletions(-)
diff --git a/core/src/layers/simulate.rs b/core/src/layers/simulate.rs
index 3f8139eb8..1d64d6408 100644
--- a/core/src/layers/simulate.rs
+++ b/core/src/layers/simulate.rs
@@ -319,11 +319,19 @@ impl<A: Access, D> SimulateDeleter<A, D> {
impl<A: Access, D: oio::Delete> oio::Delete for SimulateDeleter<A, D> {
async fn delete(&mut self, path: &str, args: OpDelete) -> Result<()> {
- if args.recursive() && self.accessor.config.delete_recursive {
- return self
- .accessor
- .simulate_delete_with_recursive(&mut self.deleter, path, args)
- .await;
+ if args.recursive() {
+ let cap = self.accessor.info.native_capability();
+
+ if cap.delete_with_recursive {
+ return self.deleter.delete(path, args).await;
+ }
+
+ if self.accessor.config.delete_recursive {
+ return self
+ .accessor
+ .simulate_delete_with_recursive(&mut self.deleter, path, args)
+ .await;
+ }
}
self.deleter.delete(path, args).await
diff --git a/core/src/services/fs/backend.rs b/core/src/services/fs/backend.rs
index 8f6ddb417..923ce2cda 100644
--- a/core/src/services/fs/backend.rs
+++ b/core/src/services/fs/backend.rs
@@ -153,6 +153,7 @@ impl Builder for FsBuilder {
create_dir: true,
delete: true,
+ delete_with_recursive: true,
list: true,
diff --git a/core/src/services/fs/deleter.rs b/core/src/services/fs/deleter.rs
index cd896aae9..bf8eeecd6 100644
--- a/core/src/services/fs/deleter.rs
+++ b/core/src/services/fs/deleter.rs
@@ -32,15 +32,23 @@ impl FsDeleter {
}
impl oio::OneShotDelete for FsDeleter {
- async fn delete_once(&self, path: String, _: OpDelete) -> Result<()> {
+ async fn delete_once(&self, path: String, args: OpDelete) -> Result<()> {
let p = self.core.root.join(path.trim_end_matches('/'));
+ let recursive = args.recursive();
+
let meta = tokio::fs::metadata(&p).await;
match meta {
Ok(meta) => {
if meta.is_dir() {
- tokio::fs::remove_dir(&p).await.map_err(new_std_io_error)?;
+ if recursive {
+ tokio::fs::remove_dir_all(&p)
+ .await
+ .map_err(new_std_io_error)?;
+ } else {
+ tokio::fs::remove_dir(&p).await.map_err(new_std_io_error)?;
+ }
} else {
tokio::fs::remove_file(&p).await.map_err(new_std_io_error)?;
}
diff --git a/core/tests/behavior/async_delete.rs b/core/tests/behavior/async_delete.rs
index e2b141727..625496075 100644
--- a/core/tests/behavior/async_delete.rs
+++ b/core/tests/behavior/async_delete.rs
@@ -39,6 +39,9 @@ pub fn tests(op: &Operator, tests: &mut Vec<Trial>) {
test_batch_delete,
test_batch_delete_with_version
));
+ if cap.delete_with_recursive {
+ tests.extend(async_trials!(op, test_delete_with_recursive_basic));
+ }
if cap.list_with_recursive {
tests.extend(async_trials!(op, test_remove_all_basic));
if !cap.create_dir {
@@ -276,6 +279,38 @@ pub async fn test_delete_with_not_existing_version(op: Operator) -> Result<()> {
Ok(())
}
+pub async fn test_delete_with_recursive_basic(op: Operator) -> Result<()> {
+ if !op.info().full_capability().delete_with_recursive {
+ return Ok(());
+ }
+
+ let base = format!("delete_recursive_{}/", uuid::Uuid::new_v4());
+
+ let files = [
+ format!("{base}file.txt"),
+ format!("{base}dir1/file1.txt"),
+ format!("{base}dir1/dir2/file2.txt"),
+ ];
+
+ for path in &files {
+ op.write(path, "delete recursive").await?;
+ }
+
+ op.delete_with(&base).recursive(true).await?;
+
+ let mut l = op.lister_with(&base).recursive(true).await?;
+ assert!(
+ l.try_next().await?.is_none(),
+ "all entries should be removed"
+ );
+
+ for path in &files {
+ assert!(!op.exists(path).await?, "{path} should be removed");
+ }
+
+ Ok(())
+}
+
pub async fn test_batch_delete(op: Operator) -> Result<()> {
let mut cap = op.info().full_capability();
if cap.delete_max_size.unwrap_or(1) <= 1 {