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 2026/08/17 07:16:31 UTC
(opendal) branch main updated: feat(bindings/dotnet): add DeleteOptions with delete functions (#8098)
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 04364ea323 feat(bindings/dotnet): add DeleteOptions with delete functions (#8098)
04364ea323 is described below
commit 04364ea32390796730099203a211357100bc517c
Author: Fatorin <sk...@gmail.com>
AuthorDate: Mon Aug 17 15:16:25 2026 +0800
feat(bindings/dotnet): add DeleteOptions with delete functions (#8098)
* feat(bindings/dotnet): add DeleteOptions
* refactor(bindings/dotnet): implement RemoveAll on top of DeleteOptions
---
.../OpenDAL.Tests/Behavior/DeleteBehaviorTest.cs | 70 ++++++++-
.../Behavior/RemoveAllBehaviorTest.cs | 4 +-
bindings/dotnet/OpenDAL/NativeMethods.cs | 48 +++---
bindings/dotnet/OpenDAL/Operator.cs | 83 +++++-----
bindings/dotnet/OpenDAL/Options/DeleteOptions.cs | 56 +++++++
bindings/dotnet/src/operator.rs | 168 +++++++++------------
bindings/dotnet/src/options.rs | 9 ++
7 files changed, 273 insertions(+), 165 deletions(-)
diff --git a/bindings/dotnet/OpenDAL.Tests/Behavior/DeleteBehaviorTest.cs b/bindings/dotnet/OpenDAL.Tests/Behavior/DeleteBehaviorTest.cs
index 7bab4c2861..43ff615c9d 100644
--- a/bindings/dotnet/OpenDAL.Tests/Behavior/DeleteBehaviorTest.cs
+++ b/bindings/dotnet/OpenDAL.Tests/Behavior/DeleteBehaviorTest.cs
@@ -17,6 +17,8 @@
* under the License.
*/
+using OpenDAL.Options;
+
namespace OpenDAL.Tests;
[Collection("BehaviorOperator")]
@@ -57,7 +59,7 @@ public sealed class DeleteBehaviorTest : BehaviorTestBase
var path = NewPath("delete-async");
await Op.WriteAsync(path, RandomBytes(12), CT);
- await Op.DeleteAsync(path, CT);
+ await Op.DeleteAsync(path, cancellationToken: CT);
var ex = await Assert.ThrowsAsync<OpenDALException>(() => Op.ReadAsync(path, CT));
Assert.True(IsMissingError(ex));
@@ -82,6 +84,70 @@ public sealed class DeleteBehaviorTest : BehaviorTestBase
return;
}
- await Op.DeleteAsync(NewPath("delete-missing-async"), CT);
+ await Op.DeleteAsync(NewPath("delete-missing-async"), cancellationToken: CT);
+ }
+
+ [Fact]
+ public async Task DeleteBehavior_WithVersion_RemovesVersionPermanentlyAsync()
+ {
+ if (!Supports(c => c.DeleteWithVersion && c.StatWithVersion && c.Write))
+ {
+ return;
+ }
+
+ var path = NewPath("delete-version");
+ await Op.WriteAsync(path, RandomBytes(16), CT);
+
+ var version = (await Op.StatAsync(path, CT)).Version;
+ Assert.NotNull(version);
+
+ await Op.DeleteAsync(path, cancellationToken: CT);
+ var missing = await Assert.ThrowsAsync<OpenDALException>(() => Op.StatAsync(path, CT));
+ Assert.True(IsMissingError(missing));
+
+ var archived = await Op.StatAsync(path, new StatOptions { Version = version }, CT);
+ Assert.Equal(version, archived.Version);
+
+ await Op.DeleteAsync(path, new DeleteOptions { Version = version }, CT);
+
+ var gone = await Assert.ThrowsAsync<OpenDALException>(
+ () => Op.StatAsync(path, new StatOptions { Version = version }, CT));
+ Assert.Equal(ErrorCode.NotFound, gone.Code);
+ }
+
+ [Fact]
+ public void DeleteBehavior_WithRecursive_RemovesTree()
+ {
+ if (!Supports(c => c.DeleteWithRecursive && c.Write && c.List))
+ {
+ return;
+ }
+
+ var dir = NewPath("delete-recursive") + "/";
+ Op.Write($"{dir}a.txt", RandomBytes(16));
+ Op.Write($"{dir}nested/b.txt", RandomBytes(16));
+
+ Op.Delete(dir, new DeleteOptions { Recursive = true });
+
+ Assert.Empty(Op.List(dir, new ListOptions { Recursive = true }));
+ }
+
+ [Fact]
+ public void DeleteBehavior_WithVersionFromAnotherObject_IsAllowed()
+ {
+ if (!Supports(c => c.DeleteWithVersion && c.StatWithVersion && c.Write))
+ {
+ return;
+ }
+
+ var donor = NewPath("delete-version-donor");
+ Op.Write(donor, RandomBytes(8));
+ var foreignVersion = Op.Stat(donor).Version;
+ Assert.NotNull(foreignVersion);
+
+ var target = NewPath("delete-version-target");
+ Op.Write(target, RandomBytes(8));
+
+ Op.Delete(target, new DeleteOptions { Version = foreignVersion });
}
}
diff --git a/bindings/dotnet/OpenDAL.Tests/Behavior/RemoveAllBehaviorTest.cs b/bindings/dotnet/OpenDAL.Tests/Behavior/RemoveAllBehaviorTest.cs
index 86ce57ad2f..16995c459a 100644
--- a/bindings/dotnet/OpenDAL.Tests/Behavior/RemoveAllBehaviorTest.cs
+++ b/bindings/dotnet/OpenDAL.Tests/Behavior/RemoveAllBehaviorTest.cs
@@ -43,7 +43,7 @@ public sealed class RemoveAllBehaviorTest : BehaviorTestBase
Op.Write($"{dir}nested/b.txt", RandomBytes(16));
Op.RemoveAll(dir);
- var entries = Op.List(dir);
+ var entries = Op.List(dir, new Options.ListOptions { Recursive = true });
Assert.Empty(entries);
}
@@ -61,7 +61,7 @@ public sealed class RemoveAllBehaviorTest : BehaviorTestBase
await Op.WriteAsync($"{dir}nested/b.txt", RandomBytes(16), CT);
await Op.RemoveAllAsync(dir, CT);
- var entries = await Op.ListAsync(dir, new OpenDAL.Options.ListOptions(), CT);
+ var entries = await Op.ListAsync(dir, new Options.ListOptions { Recursive = true }, CT);
Assert.Empty(entries);
}
}
diff --git a/bindings/dotnet/OpenDAL/NativeMethods.cs b/bindings/dotnet/OpenDAL/NativeMethods.cs
index b8dfd7e794..ede85949df 100644
--- a/bindings/dotnet/OpenDAL/NativeMethods.cs
+++ b/bindings/dotnet/OpenDAL/NativeMethods.cs
@@ -130,6 +130,22 @@ internal partial class NativeMethods
#endregion
+ #region DeleteOption
+
+ [LibraryImport(__DllName, EntryPoint = "delete_option_build", StringMarshalling = StringMarshalling.Utf8)]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ internal static partial OpenDALOptionsResult delete_option_build(
+ [In] string[] keys,
+ [In] string[] values,
+ nuint len
+ );
+
+ [LibraryImport(__DllName, EntryPoint = "delete_option_free")]
+ [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
+ internal static partial void delete_option_free(IntPtr options);
+
+ #endregion
+
#endregion
#region Layer
@@ -340,18 +356,20 @@ internal partial class NativeMethods
#region Delete
- [LibraryImport(__DllName, EntryPoint = "operator_delete", StringMarshalling = StringMarshalling.Utf8)]
+ [LibraryImport(__DllName, EntryPoint = "operator_delete_with_options", StringMarshalling = StringMarshalling.Utf8)]
[UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
- internal static partial OpenDALResult operator_delete(
+ internal static partial OpenDALResult operator_delete_with_options(
Operator op,
- string path
+ string path,
+ IntPtr options
);
- [LibraryImport(__DllName, EntryPoint = "operator_delete_async", StringMarshalling = StringMarshalling.Utf8)]
+ [LibraryImport(__DllName, EntryPoint = "operator_delete_with_options_async", StringMarshalling = StringMarshalling.Utf8)]
[UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
- internal static unsafe partial OpenDALResult operator_delete_async(
+ internal static unsafe partial OpenDALResult operator_delete_with_options_async(
Operator op,
string path,
+ IntPtr options,
delegate* unmanaged[Cdecl]<long, OpenDALResult, void> callback,
long context
);
@@ -422,26 +440,6 @@ internal partial class NativeMethods
#endregion
- #region RemoveAll
-
- [LibraryImport(__DllName, EntryPoint = "operator_remove_all", StringMarshalling = StringMarshalling.Utf8)]
- [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
- internal static partial OpenDALResult operator_remove_all(
- Operator op,
- string path
- );
-
- [LibraryImport(__DllName, EntryPoint = "operator_remove_all_async", StringMarshalling = StringMarshalling.Utf8)]
- [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]
- internal static unsafe partial OpenDALResult operator_remove_all_async(
- Operator op,
- string path,
- delegate* unmanaged[Cdecl]<long, OpenDALResult, void> callback,
- long context
- );
-
- #endregion
-
#region Presign
[LibraryImport(__DllName, EntryPoint = "operator_presign_read_async", StringMarshalling = StringMarshalling.Utf8)]
diff --git a/bindings/dotnet/OpenDAL/Operator.cs b/bindings/dotnet/OpenDAL/Operator.cs
index 2071673bb7..93dd0ce251 100644
--- a/bindings/dotnet/OpenDAL/Operator.cs
+++ b/bindings/dotnet/OpenDAL/Operator.cs
@@ -766,10 +766,12 @@ public partial class Operator : SafeHandle
/// Deletes the file at the specified path.
/// </summary>
/// <param name="path">Target path in the configured backend.</param>
- public void Delete(string path)
+ /// <param name="options">Additional delete options.</param>
+ public void Delete(string path, DeleteOptions? options = null)
{
ObjectDisposedException.ThrowIf(IsInvalid, this);
- var result = NativeMethods.operator_delete(this, path);
+ using var nativeOptionsHandle = options?.BuildNativeOptionsHandle();
+ var result = NativeMethods.operator_delete_with_options(this, path, GetOptionsHandle(nativeOptionsHandle));
ThrowIfErrorAndRelease(result);
}
@@ -777,21 +779,23 @@ public partial class Operator : SafeHandle
/// Deletes the file at the specified path asynchronously.
/// </summary>
/// <param name="path">Target path in the configured backend.</param>
+ /// <param name="options">Additional delete options.</param>
/// <param name="cancellationToken">Cancellation token for the managed task.</param>
/// <returns>A task that completes when the native callback reports completion.</returns>
- public Task DeleteAsync(string path, CancellationToken cancellationToken = default)
+ public Task DeleteAsync(string path, DeleteOptions? options = null, CancellationToken cancellationToken = default)
{
ObjectDisposedException.ThrowIf(IsInvalid, this);
- return SubmitAsyncOperation(SubmitDeleteAsync, cancellationToken);
+ return SubmitAsyncOperation(options, SubmitDeleteAsync, cancellationToken);
- OpenDALResult SubmitDeleteAsync(long context)
+ OpenDALResult SubmitDeleteAsync(long context, IntPtr optionsHandle)
{
unsafe
{
- return NativeMethods.operator_delete_async(
+ return NativeMethods.operator_delete_with_options_async(
this,
path,
+ optionsHandle,
&OnDeleteCompleted,
context
);
@@ -928,9 +932,8 @@ public partial class Operator : SafeHandle
/// <param name="path">Target path in the configured backend.</param>
public void RemoveAll(string path)
{
- ObjectDisposedException.ThrowIf(IsInvalid, this);
- var result = NativeMethods.operator_remove_all(this, path);
- ThrowIfErrorAndRelease(result);
+ var options = new DeleteOptions { Recursive = true };
+ Delete(path, options);
}
/// <summary>
@@ -941,22 +944,8 @@ public partial class Operator : SafeHandle
/// <returns>A task that completes when the native callback reports completion.</returns>
public Task RemoveAllAsync(string path, CancellationToken cancellationToken = default)
{
- ObjectDisposedException.ThrowIf(IsInvalid, this);
-
- return SubmitAsyncOperation(SubmitRemoveAllAsync, cancellationToken);
-
- OpenDALResult SubmitRemoveAllAsync(long context)
- {
- unsafe
- {
- return NativeMethods.operator_remove_all_async(
- this,
- path,
- &OnRemoveAllCompleted,
- context
- );
- }
- }
+ var options = new DeleteOptions { Recursive = true };
+ return DeleteAsync(path, options, cancellationToken);
}
/// <summary>
@@ -1291,6 +1280,39 @@ public partial class Operator : SafeHandle
}
}
+ /// <summary>
+ /// Submits a native async operation and binds it to a managed task completion source.
+ /// </summary>
+ /// <typeparam name="TOptions">Managed options type.</typeparam>
+ /// <param name="options">Optional managed options for this operation.</param>
+ /// <param name="submit">Submission delegate that invokes the native async API.</param>
+ /// <param name="cancellationToken">Cancellation token for managed task observation.</param>
+ /// <returns>A task completed by the corresponding native callback.</returns>
+ /// <exception cref="OperationCanceledException"><paramref name="cancellationToken"/> is already canceled.</exception>
+ /// <exception cref="OpenDALException">Native submission returns an immediate error.</exception>
+ internal static Task SubmitAsyncOperation<TOptions>(
+ TOptions? options,
+ Func<long, IntPtr, OpenDALResult> submit,
+ CancellationToken cancellationToken)
+ where TOptions : class, IOptions
+ {
+ cancellationToken.ThrowIfCancellationRequested();
+ var context = AsyncStateRegistry.Register<bool>(out var asyncState);
+ try
+ {
+ using var nativeOptionsHandle = options?.BuildNativeOptionsHandle();
+ var submitResult = submit(context, GetOptionsHandle(nativeOptionsHandle));
+ ThrowIfErrorAndRelease(submitResult);
+ asyncState.BindCancellation(cancellationToken);
+ return asyncState.Completion.Task;
+ }
+ catch
+ {
+ AsyncStateRegistry.Unregister(context);
+ throw;
+ }
+ }
+
/// <summary>
/// Submits a native async operation without options and binds it to a managed task completion source.
/// </summary>
@@ -1549,17 +1571,6 @@ public partial class Operator : SafeHandle
CompleteAsyncCallback(context, result);
}
- /// <summary>
- /// Native callback invoked when an asynchronous remove-all operation finishes.
- /// </summary>
- /// <param name="context">Opaque async state context previously registered by <see cref="AsyncStateRegistry"/>.</param>
- /// <param name="result">Remove-all completion result returned by the native layer.</param>
- [UnmanagedCallersOnly(CallConvs = [typeof(CallConvCdecl)])]
- private static void OnRemoveAllCompleted(long context, OpenDALResult result)
- {
- CompleteAsyncCallback(context, result);
- }
-
/// <summary>
/// Native callback invoked when an asynchronous presign-read operation finishes.
/// </summary>
diff --git a/bindings/dotnet/OpenDAL/Options/DeleteOptions.cs b/bindings/dotnet/OpenDAL/Options/DeleteOptions.cs
new file mode 100644
index 0000000000..dcb9ebfc47
--- /dev/null
+++ b/bindings/dotnet/OpenDAL/Options/DeleteOptions.cs
@@ -0,0 +1,56 @@
+/*
+ * 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.
+ */
+
+using OpenDAL.Options.Abstractions;
+
+namespace OpenDAL.Options;
+
+/// <summary>
+/// Options for delete operations.
+/// </summary>
+public sealed class DeleteOptions : IOptions
+{
+ /// <summary>
+ /// The version of the file to delete.
+ /// </summary>
+ public string? Version { get; init; }
+
+ /// <summary>
+ /// Whether to delete the target recursively.
+ /// </summary>
+ /// <remarks>
+ /// If `false`, behaves like the traditional single-object delete.
+ /// If `true`, all entries under the path (or sharing the prefix for file-like paths) will be removed.
+ /// </remarks>
+ public bool Recursive { get; init; }
+
+ public NativeOptionsHandle BuildNativeOptionsHandle()
+ {
+ var nativeOptions = new NativeOptionsBuilder()
+ .AddString("version", Version)
+ .AddBoolTrue("recursive", Recursive)
+ .Build();
+
+ return NativeOptionsBuilder.BuildNativeOptionsHandle(
+ nativeOptions,
+ NativeMethods.delete_option_build,
+ NativeMethods.delete_option_free
+ );
+ }
+}
\ No newline at end of file
diff --git a/bindings/dotnet/src/operator.rs b/bindings/dotnet/src/operator.rs
index 1b705bea10..fbda6e4287 100644
--- a/bindings/dotnet/src/operator.rs
+++ b/bindings/dotnet/src/operator.rs
@@ -22,7 +22,10 @@ use crate::{
executor::executor_or_default,
metadata::OpendalMetadata,
operator_info::{OpendalOperatorInfo, into_operator_info},
- options::{parse_list_options, parse_read_options, parse_stat_options, parse_write_options},
+ options::{
+ parse_delete_options, parse_list_options, parse_read_options, parse_stat_options,
+ parse_write_options,
+ },
presign::into_presigned_request_ptr,
result::{
OpendalEntryListResult, OpendalMetadataResult, OpendalOperatorInfoResult,
@@ -267,6 +270,42 @@ pub unsafe extern "C" fn list_option_free(options: *mut opendal::options::ListOp
}
}
+/// Build delete options from raw C string key/value arrays.
+///
+/// On success, the returned pointer must be released by `delete_option_free`.
+/// # Safety
+///
+/// - When `len > 0`, `keys` and `values` must be non-null pointers to arrays
+/// containing at least `len` C-string pointers.
+/// - Each key/value entry must be a valid null-terminated UTF-8 string.
+#[unsafe(no_mangle)]
+pub unsafe extern "C" fn delete_option_build(
+ keys: *const *const c_char,
+ values: *const *const c_char,
+ len: usize,
+) -> OpendalOptionsResult {
+ match unsafe { collect_options(keys, values, len) }
+ .and_then(|values| parse_delete_options(&values))
+ {
+ Ok(options) => OpendalOptionsResult::ok(Box::into_raw(Box::new(options)) as *mut c_void),
+ Err(error) => OpendalOptionsResult::from_error(error),
+ }
+}
+
+/// # Safety
+///
+/// - `options` must be null or a pointer returned by `delete_option_build`.
+/// - This function must be called at most once for the same pointer.
+#[unsafe(no_mangle)]
+pub unsafe extern "C" fn delete_option_free(options: *mut opendal::options::DeleteOptions) {
+ if options.is_null() {
+ return;
+ }
+ unsafe {
+ drop(Box::from_raw(options));
+ }
+}
+
/// Construct an OpenDAL operator instance from a scheme and key/value options.
///
/// Returns a pointer that must be released with `operator_free`.
@@ -623,59 +662,71 @@ fn operator_duplicate_inner(op_handle: *const OperatorHandle) -> Result<*mut c_v
Ok(Box::into_raw(Box::new(handle.with_operator(handle.operator()))) as *mut c_void)
}
-/// Delete `path` synchronously.
+/// Delete `path` synchronously with options.
/// # Safety
///
/// - `op_handle` must be a valid operator pointer from `operator_construct`.
/// - `path` must be a valid null-terminated UTF-8 string.
+/// - `options` must be null or a pointer returned by `delete_option_build`.
#[unsafe(no_mangle)]
-pub extern "C" fn operator_delete(
+pub extern "C" fn operator_delete_with_options(
op_handle: *const OperatorHandle,
path: *const c_char,
+ options: *const opendal::options::DeleteOptions,
) -> OpendalResult {
- match operator_delete_inner(op_handle, path) {
+ match operator_delete_with_options_inner(op_handle, path, options) {
Ok(()) => OpendalResult::ok(),
Err(error) => OpendalResult::from_error(error),
}
}
-fn operator_delete_inner(
+fn operator_delete_with_options_inner(
op_handle: *const OperatorHandle,
path: *const c_char,
+ options: *const opendal::options::DeleteOptions,
) -> Result<(), OpenDALError> {
let handle = require_op_handle(op_handle)?;
let executor = handle.executor.clone();
let path = require_cstr(path, "path")?;
+ let options = if options.is_null() {
+ opendal::options::DeleteOptions::default()
+ } else {
+ unsafe { (&*options).clone() }
+ };
+
executor
- .block_on(handle.delete(path))
+ .block_on(handle.delete_options(path, options))
.map_err(OpenDALError::from_opendal_error)
}
-/// Delete `path` asynchronously.
+/// Delete `path` asynchronously with options.
///
/// The callback is invoked exactly once with the final result.
/// # Safety
///
/// - `op_handle` must be a valid operator pointer from `operator_construct`.
/// - `path` must be a valid null-terminated UTF-8 string.
+/// - `options` must be null or a pointer returned by `delete_option_build`.
/// - `callback` must be a valid function pointer and remain callable until invoked.
#[unsafe(no_mangle)]
-pub extern "C" fn operator_delete_async(
+pub extern "C" fn operator_delete_with_options_async(
op_handle: *const OperatorHandle,
path: *const c_char,
+ options: *const opendal::options::DeleteOptions,
callback: Option<WriteCallback>,
context: i64,
) -> OpendalResult {
- match operator_delete_async_inner(op_handle, path, callback, context) {
+ match operator_delete_with_options_async_inner(op_handle, path, options, callback, context) {
Ok(()) => OpendalResult::ok(),
Err(error) => OpendalResult::from_error(error),
}
}
-fn operator_delete_async_inner(
+fn operator_delete_with_options_async_inner(
op_handle: *const OperatorHandle,
path: *const c_char,
+ options: *const opendal::options::DeleteOptions,
callback: Option<WriteCallback>,
context: i64,
) -> Result<(), OpenDALError> {
@@ -684,10 +735,16 @@ fn operator_delete_async_inner(
let path = require_cstr(path, "path")?.to_string();
let callback = require_callback(callback)?;
+ let options = if options.is_null() {
+ opendal::options::DeleteOptions::default()
+ } else {
+ unsafe { (&*options).clone() }
+ };
+
let op = handle.operator();
executor.spawn(async move {
let result = op
- .delete(&path)
+ .delete_options(&path, options)
.await
.map_err(OpenDALError::from_opendal_error);
@@ -957,95 +1014,6 @@ fn operator_rename_async_inner(
Ok(())
}
-/// Remove `path` recursively synchronously.
-/// # Safety
-///
-/// - `op_handle` must be a valid operator pointer from `operator_construct`.
-/// - `path` must be a valid null-terminated UTF-8 string.
-#[unsafe(no_mangle)]
-pub extern "C" fn operator_remove_all(
- op_handle: *const OperatorHandle,
- path: *const c_char,
-) -> OpendalResult {
- match operator_remove_all_inner(op_handle, path) {
- Ok(()) => OpendalResult::ok(),
- Err(error) => OpendalResult::from_error(error),
- }
-}
-
-fn operator_remove_all_inner(
- op_handle: *const OperatorHandle,
- path: *const c_char,
-) -> Result<(), OpenDALError> {
- let handle = require_op_handle(op_handle)?;
- let executor = handle.executor.clone();
- let path = require_cstr(path, "path")?;
-
- let options = opendal::options::DeleteOptions {
- recursive: true,
- ..Default::default()
- };
-
- executor
- .block_on(handle.delete_options(path, options))
- .map_err(OpenDALError::from_opendal_error)
-}
-
-/// Remove `path` recursively asynchronously.
-///
-/// The callback is invoked exactly once with the final result.
-/// # Safety
-///
-/// - `op_handle` must be a valid operator pointer from `operator_construct`.
-/// - `path` must be a valid null-terminated UTF-8 string.
-/// - `callback` must be a valid function pointer and remain callable until invoked.
-#[unsafe(no_mangle)]
-pub extern "C" fn operator_remove_all_async(
- op_handle: *const OperatorHandle,
- path: *const c_char,
- callback: Option<WriteCallback>,
- context: i64,
-) -> OpendalResult {
- match operator_remove_all_async_inner(op_handle, path, callback, context) {
- Ok(()) => OpendalResult::ok(),
- Err(error) => OpendalResult::from_error(error),
- }
-}
-
-fn operator_remove_all_async_inner(
- op_handle: *const OperatorHandle,
- path: *const c_char,
- callback: Option<WriteCallback>,
- context: i64,
-) -> Result<(), OpenDALError> {
- let handle = require_op_handle(op_handle)?;
- let executor = handle.executor.clone();
- let path = require_cstr(path, "path")?.to_string();
- let callback = require_callback(callback)?;
- let options = opendal::options::DeleteOptions {
- recursive: true,
- ..Default::default()
- };
-
- let op = handle.operator();
- executor.spawn(async move {
- let result = op
- .delete_options(&path, options)
- .await
- .map_err(OpenDALError::from_opendal_error);
-
- callback(
- context,
- match result {
- Ok(()) => OpendalResult::ok(),
- Err(error) => OpendalResult::from_error(error),
- },
- );
- });
-
- Ok(())
-}
-
/// Presign read asynchronously.
///
/// The callback is invoked exactly once with the final result.
diff --git a/bindings/dotnet/src/options.rs b/bindings/dotnet/src/options.rs
index 3648f8a93c..0a84e0eadf 100644
--- a/bindings/dotnet/src/options.rs
+++ b/bindings/dotnet/src/options.rs
@@ -210,3 +210,12 @@ pub fn parse_list_options(
Ok(options)
}
+
+pub fn parse_delete_options(
+ values: &HashMap<String, String>,
+) -> Result<opendal::options::DeleteOptions, OpenDALError> {
+ Ok(opendal::options::DeleteOptions {
+ version: parse_string(values, "version"),
+ recursive: parse_bool(values, "recursive")?.unwrap_or(false),
+ })
+}