You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@opendal.apache.org by te...@apache.org on 2026/05/01 18:50:06 UTC
(opendal) 01/01: feat(bindings/dart): add File.read and File.write with error mapping
This is an automated email from the ASF dual-hosted git repository.
TennyZhuang pushed a commit to branch agent-dart-readwrite
in repository https://gitbox.apache.org/repos/asf/opendal.git
commit 33ed9ebeb14d52b492f76b9c648455d85c23ba4a
Author: tianyizhuang <ti...@tianyizhuangs-Mac-mini.local>
AuthorDate: Sat May 2 02:41:26 2026 +0800
feat(bindings/dart): add File.read and File.write with error mapping
Add read/write async and sync methods to the Dart File class,
mirroring Rust Operator::read and Operator::write.
Changes:
- rust/src/api/opendal_api.rs: add read/read_sync/write/write_sync
returning anyhow::Result so Rust errors surface as Dart exceptions
- rust/Cargo.toml: add anyhow = "1.0" dependency
- lib/opendal.dart: add File.read()/readSync()/write()/writeSync()
- tests/opendal_test.dart: add round-trip, missing-file throws, and
overwrite semantics tests
- Regenerate FRB bindings for new methods
Depends on FRB 2.12.0 upgrade (PR A).
Scope: bindings/dart only.
AI-assisted: od-kimi-scout-050121.
---
bindings/dart/lib/opendal.dart | 17 ++
bindings/dart/lib/src/rust/api/opendal_api.dart | 8 +
bindings/dart/lib/src/rust/frb_generated.dart | 190 +++++++++++++++-
bindings/dart/lib/src/rust/frb_generated.io.dart | 19 ++
bindings/dart/lib/src/rust/frb_generated.web.dart | 19 ++
bindings/dart/rust/Cargo.toml | 1 +
bindings/dart/rust/src/api/opendal_api.rs | 22 ++
bindings/dart/rust/src/frb_generated.rs | 253 +++++++++++++++++++++-
bindings/dart/tests/opendal_test.dart | 34 +++
9 files changed, 553 insertions(+), 10 deletions(-)
diff --git a/bindings/dart/lib/opendal.dart b/bindings/dart/lib/opendal.dart
index 1c7d73081..47c6999cd 100644
--- a/bindings/dart/lib/opendal.dart
+++ b/bindings/dart/lib/opendal.dart
@@ -16,6 +16,7 @@
// under the License.
import 'dart:io' as io;
+import 'dart:typed_data';
import 'package:system_info2/system_info2.dart';
import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated_io.dart';
import 'src/rust/frb_generated.dart';
@@ -148,6 +149,22 @@ class File {
void deleteSync() {
_operator.deleteSync(path: path);
}
+
+ Future<Uint8List> read() {
+ return _operator.read(path: path);
+ }
+
+ Uint8List readSync() {
+ return _operator.readSync(path: path);
+ }
+
+ Future<void> write(Uint8List data) {
+ return _operator.write(path: path, data: data);
+ }
+
+ void writeSync(Uint8List data) {
+ _operator.writeSync(path: path, data: data);
+ }
}
class Directory {
diff --git a/bindings/dart/lib/src/rust/api/opendal_api.dart b/bindings/dart/lib/src/rust/api/opendal_api.dart
index 9040eb51b..640f03372 100644
--- a/bindings/dart/lib/src/rust/api/opendal_api.dart
+++ b/bindings/dart/lib/src/rust/api/opendal_api.dart
@@ -56,6 +56,10 @@ abstract class Operator implements RustOpaqueInterface {
RustLib.instance.api
.crateApiOpendalApiOperatorNew(scheme: scheme, map: map);
+ Future<Uint8List> read({required String path});
+
+ Uint8List readSync({required String path});
+
Future<void> rename({required String from, required String to});
void renameSync({required String from, required String to});
@@ -63,4 +67,8 @@ abstract class Operator implements RustOpaqueInterface {
Future<Metadata> stat({required String path});
Metadata statSync({required String path});
+
+ Future<void> write({required String path, required List<int> data});
+
+ void writeSync({required String path, required List<int> data});
}
diff --git a/bindings/dart/lib/src/rust/frb_generated.dart b/bindings/dart/lib/src/rust/frb_generated.dart
index 2936c2a6f..c34acb21c 100644
--- a/bindings/dart/lib/src/rust/frb_generated.dart
+++ b/bindings/dart/lib/src/rust/frb_generated.dart
@@ -68,7 +68,7 @@ class RustLib extends BaseEntrypoint<RustLibApi, RustLibApiImpl, RustLibWire> {
String get codegenVersion => '2.12.0';
@override
- int get rustContentHash => 775125233;
+ int get rustContentHash => 798133092;
static const kDefaultExternalLibraryLoaderConfig =
ExternalLibraryLoaderConfig(
@@ -120,6 +120,12 @@ abstract class RustLibApi extends BaseApi {
Operator crateApiOpendalApiOperatorNew(
{required String scheme, required Map<String, String> map});
+ Future<Uint8List> crateApiOpendalApiOperatorRead(
+ {required Operator that, required String path});
+
+ Uint8List crateApiOpendalApiOperatorReadSync(
+ {required Operator that, required String path});
+
Future<void> crateApiOpendalApiOperatorRename(
{required Operator that, required String from, required String to});
@@ -132,6 +138,12 @@ abstract class RustLibApi extends BaseApi {
Metadata crateApiOpendalApiOperatorStatSync(
{required Operator that, required String path});
+ Future<void> crateApiOpendalApiOperatorWrite(
+ {required Operator that, required String path, required List<int> data});
+
+ void crateApiOpendalApiOperatorWriteSync(
+ {required Operator that, required String path, required List<int> data});
+
RustArcIncrementStrongCountFnType
get rust_arc_increment_strong_count_Metadata;
@@ -576,6 +588,61 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
argNames: ["scheme", "map"],
);
+ @override
+ Future<Uint8List> crateApiOpendalApiOperatorRead(
+ {required Operator that, required String path}) {
+ return handler.executeNormal(NormalTask(
+ callFfi: (port_) {
+ final serializer = SseSerializer(generalizedFrbRustBinding);
+ sse_encode_Auto_Ref_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerOperator(
+ that, serializer);
+ sse_encode_String(path, serializer);
+ pdeCallFfi(generalizedFrbRustBinding, serializer,
+ funcId: 17, port: port_);
+ },
+ codec: SseCodec(
+ decodeSuccessData: sse_decode_list_prim_u_8_strict,
+ decodeErrorData: sse_decode_AnyhowException,
+ ),
+ constMeta: kCrateApiOpendalApiOperatorReadConstMeta,
+ argValues: [that, path],
+ apiImpl: this,
+ ));
+ }
+
+ TaskConstMeta get kCrateApiOpendalApiOperatorReadConstMeta =>
+ const TaskConstMeta(
+ debugName: "Operator_read",
+ argNames: ["that", "path"],
+ );
+
+ @override
+ Uint8List crateApiOpendalApiOperatorReadSync(
+ {required Operator that, required String path}) {
+ return handler.executeSync(SyncTask(
+ callFfi: () {
+ final serializer = SseSerializer(generalizedFrbRustBinding);
+ sse_encode_Auto_Ref_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerOperator(
+ that, serializer);
+ sse_encode_String(path, serializer);
+ return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 18)!;
+ },
+ codec: SseCodec(
+ decodeSuccessData: sse_decode_list_prim_u_8_strict,
+ decodeErrorData: sse_decode_AnyhowException,
+ ),
+ constMeta: kCrateApiOpendalApiOperatorReadSyncConstMeta,
+ argValues: [that, path],
+ apiImpl: this,
+ ));
+ }
+
+ TaskConstMeta get kCrateApiOpendalApiOperatorReadSyncConstMeta =>
+ const TaskConstMeta(
+ debugName: "Operator_read_sync",
+ argNames: ["that", "path"],
+ );
+
@override
Future<void> crateApiOpendalApiOperatorRename(
{required Operator that, required String from, required String to}) {
@@ -587,7 +654,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
sse_encode_String(from, serializer);
sse_encode_String(to, serializer);
pdeCallFfi(generalizedFrbRustBinding, serializer,
- funcId: 17, port: port_);
+ funcId: 19, port: port_);
},
codec: SseCodec(
decodeSuccessData: sse_decode_unit,
@@ -615,7 +682,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
that, serializer);
sse_encode_String(from, serializer);
sse_encode_String(to, serializer);
- return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 18)!;
+ return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 20)!;
},
codec: SseCodec(
decodeSuccessData: sse_decode_unit,
@@ -643,7 +710,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
that, serializer);
sse_encode_String(path, serializer);
pdeCallFfi(generalizedFrbRustBinding, serializer,
- funcId: 19, port: port_);
+ funcId: 21, port: port_);
},
codec: SseCodec(
decodeSuccessData:
@@ -671,7 +738,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
sse_encode_Auto_Ref_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerOperator(
that, serializer);
sse_encode_String(path, serializer);
- return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 20)!;
+ return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 22)!;
},
codec: SseCodec(
decodeSuccessData:
@@ -690,6 +757,63 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
argNames: ["that", "path"],
);
+ @override
+ Future<void> crateApiOpendalApiOperatorWrite(
+ {required Operator that, required String path, required List<int> data}) {
+ return handler.executeNormal(NormalTask(
+ callFfi: (port_) {
+ final serializer = SseSerializer(generalizedFrbRustBinding);
+ sse_encode_Auto_Ref_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerOperator(
+ that, serializer);
+ sse_encode_String(path, serializer);
+ sse_encode_list_prim_u_8_loose(data, serializer);
+ pdeCallFfi(generalizedFrbRustBinding, serializer,
+ funcId: 23, port: port_);
+ },
+ codec: SseCodec(
+ decodeSuccessData: sse_decode_unit,
+ decodeErrorData: sse_decode_AnyhowException,
+ ),
+ constMeta: kCrateApiOpendalApiOperatorWriteConstMeta,
+ argValues: [that, path, data],
+ apiImpl: this,
+ ));
+ }
+
+ TaskConstMeta get kCrateApiOpendalApiOperatorWriteConstMeta =>
+ const TaskConstMeta(
+ debugName: "Operator_write",
+ argNames: ["that", "path", "data"],
+ );
+
+ @override
+ void crateApiOpendalApiOperatorWriteSync(
+ {required Operator that, required String path, required List<int> data}) {
+ return handler.executeSync(SyncTask(
+ callFfi: () {
+ final serializer = SseSerializer(generalizedFrbRustBinding);
+ sse_encode_Auto_Ref_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerOperator(
+ that, serializer);
+ sse_encode_String(path, serializer);
+ sse_encode_list_prim_u_8_loose(data, serializer);
+ return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 24)!;
+ },
+ codec: SseCodec(
+ decodeSuccessData: sse_decode_unit,
+ decodeErrorData: sse_decode_AnyhowException,
+ ),
+ constMeta: kCrateApiOpendalApiOperatorWriteSyncConstMeta,
+ argValues: [that, path, data],
+ apiImpl: this,
+ ));
+ }
+
+ TaskConstMeta get kCrateApiOpendalApiOperatorWriteSyncConstMeta =>
+ const TaskConstMeta(
+ debugName: "Operator_write_sync",
+ argNames: ["that", "path", "data"],
+ );
+
RustArcIncrementStrongCountFnType
get rust_arc_increment_strong_count_Metadata => wire
.rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerMetadata;
@@ -706,6 +830,12 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
get rust_arc_decrement_strong_count_Operator => wire
.rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerOperator;
+ @protected
+ AnyhowException dco_decode_AnyhowException(dynamic raw) {
+ // Codec=Dco (DartCObject based), see doc to use other codecs
+ return AnyhowException(raw as String);
+ }
+
@protected
Metadata
dco_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerMetadata(
@@ -779,6 +909,12 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
return dco_decode_u_64(raw);
}
+ @protected
+ List<int> dco_decode_list_prim_u_8_loose(dynamic raw) {
+ // Codec=Dco (DartCObject based), see doc to use other codecs
+ return raw as List<int>;
+ }
+
@protected
Uint8List dco_decode_list_prim_u_8_strict(dynamic raw) {
// Codec=Dco (DartCObject based), see doc to use other codecs
@@ -840,6 +976,13 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
return dcoDecodeU64(raw);
}
+ @protected
+ AnyhowException sse_decode_AnyhowException(SseDeserializer deserializer) {
+ // Codec=Sse (Serialization based), see doc to use other codecs
+ var inner = sse_decode_String(deserializer);
+ return AnyhowException(inner);
+ }
+
@protected
Metadata
sse_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerMetadata(
@@ -921,6 +1064,13 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
return (sse_decode_u_64(deserializer));
}
+ @protected
+ List<int> sse_decode_list_prim_u_8_loose(SseDeserializer deserializer) {
+ // Codec=Sse (Serialization based), see doc to use other codecs
+ var len_ = sse_decode_i_32(deserializer);
+ return deserializer.buffer.getUint8List(len_);
+ }
+
@protected
Uint8List sse_decode_list_prim_u_8_strict(SseDeserializer deserializer) {
// Codec=Sse (Serialization based), see doc to use other codecs
@@ -1001,6 +1151,13 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
return deserializer.buffer.getInt32();
}
+ @protected
+ void sse_encode_AnyhowException(
+ AnyhowException self, SseSerializer serializer) {
+ // Codec=Sse (Serialization based), see doc to use other codecs
+ sse_encode_String(self.message, serializer);
+ }
+
@protected
void
sse_encode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerMetadata(
@@ -1081,6 +1238,15 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
sse_encode_u_64(self, serializer);
}
+ @protected
+ void sse_encode_list_prim_u_8_loose(
+ List<int> self, SseSerializer serializer) {
+ // Codec=Sse (Serialization based), see doc to use other codecs
+ sse_encode_i_32(self.length, serializer);
+ serializer.buffer
+ .putUint8List(self is Uint8List ? self : Uint8List.fromList(self));
+ }
+
@protected
void sse_encode_list_prim_u_8_strict(
Uint8List self, SseSerializer serializer) {
@@ -1266,6 +1432,12 @@ class OperatorImpl extends RustOpaque implements Operator {
bool existsSync({required String path}) => RustLib.instance.api
.crateApiOpendalApiOperatorExistsSync(that: this, path: path);
+ Future<Uint8List> read({required String path}) => RustLib.instance.api
+ .crateApiOpendalApiOperatorRead(that: this, path: path);
+
+ Uint8List readSync({required String path}) => RustLib.instance.api
+ .crateApiOpendalApiOperatorReadSync(that: this, path: path);
+
Future<void> rename({required String from, required String to}) =>
RustLib.instance.api
.crateApiOpendalApiOperatorRename(that: this, from: from, to: to);
@@ -1279,4 +1451,12 @@ class OperatorImpl extends RustOpaque implements Operator {
Metadata statSync({required String path}) => RustLib.instance.api
.crateApiOpendalApiOperatorStatSync(that: this, path: path);
+
+ Future<void> write({required String path, required List<int> data}) =>
+ RustLib.instance.api
+ .crateApiOpendalApiOperatorWrite(that: this, path: path, data: data);
+
+ void writeSync({required String path, required List<int> data}) => RustLib
+ .instance.api
+ .crateApiOpendalApiOperatorWriteSync(that: this, path: path, data: data);
}
diff --git a/bindings/dart/lib/src/rust/frb_generated.io.dart b/bindings/dart/lib/src/rust/frb_generated.io.dart
index ea65c86c3..18f21f390 100644
--- a/bindings/dart/lib/src/rust/frb_generated.io.dart
+++ b/bindings/dart/lib/src/rust/frb_generated.io.dart
@@ -24,6 +24,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
CrossPlatformFinalizerArg get rust_arc_decrement_strong_count_OperatorPtr => wire
._rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerOperatorPtr;
+ @protected
+ AnyhowException dco_decode_AnyhowException(dynamic raw);
+
@protected
Metadata
dco_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerMetadata(
@@ -66,6 +69,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
@protected
BigInt dco_decode_box_autoadd_u_64(dynamic raw);
+ @protected
+ List<int> dco_decode_list_prim_u_8_loose(dynamic raw);
+
@protected
Uint8List dco_decode_list_prim_u_8_strict(dynamic raw);
@@ -93,6 +99,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
@protected
BigInt dco_decode_usize(dynamic raw);
+ @protected
+ AnyhowException sse_decode_AnyhowException(SseDeserializer deserializer);
+
@protected
Metadata
sse_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerMetadata(
@@ -136,6 +145,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
@protected
BigInt sse_decode_box_autoadd_u_64(SseDeserializer deserializer);
+ @protected
+ List<int> sse_decode_list_prim_u_8_loose(SseDeserializer deserializer);
+
@protected
Uint8List sse_decode_list_prim_u_8_strict(SseDeserializer deserializer);
@@ -168,6 +180,10 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
@protected
int sse_decode_i_32(SseDeserializer deserializer);
+ @protected
+ void sse_encode_AnyhowException(
+ AnyhowException self, SseSerializer serializer);
+
@protected
void
sse_encode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerMetadata(
@@ -211,6 +227,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
@protected
void sse_encode_box_autoadd_u_64(BigInt self, SseSerializer serializer);
+ @protected
+ void sse_encode_list_prim_u_8_loose(List<int> self, SseSerializer serializer);
+
@protected
void sse_encode_list_prim_u_8_strict(
Uint8List self, SseSerializer serializer);
diff --git a/bindings/dart/lib/src/rust/frb_generated.web.dart b/bindings/dart/lib/src/rust/frb_generated.web.dart
index c2a3b5c78..c08ea8ada 100644
--- a/bindings/dart/lib/src/rust/frb_generated.web.dart
+++ b/bindings/dart/lib/src/rust/frb_generated.web.dart
@@ -26,6 +26,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
CrossPlatformFinalizerArg get rust_arc_decrement_strong_count_OperatorPtr => wire
.rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerOperator;
+ @protected
+ AnyhowException dco_decode_AnyhowException(dynamic raw);
+
@protected
Metadata
dco_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerMetadata(
@@ -68,6 +71,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
@protected
BigInt dco_decode_box_autoadd_u_64(dynamic raw);
+ @protected
+ List<int> dco_decode_list_prim_u_8_loose(dynamic raw);
+
@protected
Uint8List dco_decode_list_prim_u_8_strict(dynamic raw);
@@ -95,6 +101,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
@protected
BigInt dco_decode_usize(dynamic raw);
+ @protected
+ AnyhowException sse_decode_AnyhowException(SseDeserializer deserializer);
+
@protected
Metadata
sse_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerMetadata(
@@ -138,6 +147,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
@protected
BigInt sse_decode_box_autoadd_u_64(SseDeserializer deserializer);
+ @protected
+ List<int> sse_decode_list_prim_u_8_loose(SseDeserializer deserializer);
+
@protected
Uint8List sse_decode_list_prim_u_8_strict(SseDeserializer deserializer);
@@ -170,6 +182,10 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
@protected
int sse_decode_i_32(SseDeserializer deserializer);
+ @protected
+ void sse_encode_AnyhowException(
+ AnyhowException self, SseSerializer serializer);
+
@protected
void
sse_encode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerMetadata(
@@ -213,6 +229,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
@protected
void sse_encode_box_autoadd_u_64(BigInt self, SseSerializer serializer);
+ @protected
+ void sse_encode_list_prim_u_8_loose(List<int> self, SseSerializer serializer);
+
@protected
void sse_encode_list_prim_u_8_strict(
Uint8List self, SseSerializer serializer);
diff --git a/bindings/dart/rust/Cargo.toml b/bindings/dart/rust/Cargo.toml
index 176221aeb..84c15d573 100644
--- a/bindings/dart/rust/Cargo.toml
+++ b/bindings/dart/rust/Cargo.toml
@@ -21,6 +21,7 @@ name = "opendal-dart"
version = "0.1.0"
[dependencies]
+anyhow = "1.0"
flutter_rust_bridge = "=2.12.0"
opendal = { path = "../../../core", features = [
"blocking",
diff --git a/bindings/dart/rust/src/api/opendal_api.rs b/bindings/dart/rust/src/api/opendal_api.rs
index 70add349d..1f3b15588 100644
--- a/bindings/dart/rust/src/api/opendal_api.rs
+++ b/bindings/dart/rust/src/api/opendal_api.rs
@@ -90,6 +90,28 @@ impl Operator {
pub fn rename_sync(&self, from: String, to: String) -> () {
self.blocking_op.rename(&from, &to).unwrap()
}
+
+ pub async fn read(&self, path: String) -> anyhow::Result<Vec<u8>> {
+ let buf = self.async_op.read(&path).await.map_err(|e| anyhow::anyhow!(e))?;
+ Ok(buf.to_vec())
+ }
+
+ #[frb(sync)]
+ pub fn read_sync(&self, path: String) -> anyhow::Result<Vec<u8>> {
+ let buf = self.blocking_op.read(&path).map_err(|e| anyhow::anyhow!(e))?;
+ Ok(buf.to_vec())
+ }
+
+ pub async fn write(&self, path: String, data: Vec<u8>) -> anyhow::Result<()> {
+ self.async_op.write(&path, data).await.map_err(|e| anyhow::anyhow!(e))?;
+ Ok(())
+ }
+
+ #[frb(sync)]
+ pub fn write_sync(&self, path: String, data: Vec<u8>) -> anyhow::Result<()> {
+ self.blocking_op.write(&path, data).map_err(|e| anyhow::anyhow!(e))?;
+ Ok(())
+ }
}
#[frb(opaque)]
diff --git a/bindings/dart/rust/src/frb_generated.rs b/bindings/dart/rust/src/frb_generated.rs
index d26fd8fc2..e3e563944 100644
--- a/bindings/dart/rust/src/frb_generated.rs
+++ b/bindings/dart/rust/src/frb_generated.rs
@@ -39,7 +39,7 @@ flutter_rust_bridge::frb_generated_boilerplate!(
default_rust_auto_opaque = RustAutoOpaqueMoi,
);
pub(crate) const FLUTTER_RUST_BRIDGE_CODEGEN_VERSION: &str = "2.12.0";
-pub(crate) const FLUTTER_RUST_BRIDGE_CODEGEN_CONTENT_HASH: i32 = 775125233;
+pub(crate) const FLUTTER_RUST_BRIDGE_CODEGEN_CONTENT_HASH: i32 = 798133092;
// Section: executor
@@ -848,6 +848,114 @@ fn wire__crate__api__opendal_api__Operator_new_impl(
},
)
}
+fn wire__crate__api__opendal_api__Operator_read_impl(
+ port_: flutter_rust_bridge::for_generated::MessagePort,
+ ptr_: flutter_rust_bridge::for_generated::PlatformGeneralizedUint8ListPtr,
+ rust_vec_len_: i32,
+ data_len_: i32,
+) {
+ FLUTTER_RUST_BRIDGE_HANDLER.wrap_async::<flutter_rust_bridge::for_generated::SseCodec, _, _, _>(
+ flutter_rust_bridge::for_generated::TaskInfo {
+ debug_name: "Operator_read",
+ port: Some(port_),
+ mode: flutter_rust_bridge::for_generated::FfiCallMode::Normal,
+ },
+ move || {
+ let message = unsafe {
+ flutter_rust_bridge::for_generated::Dart2RustMessageSse::from_wire(
+ ptr_,
+ rust_vec_len_,
+ data_len_,
+ )
+ };
+ let mut deserializer =
+ flutter_rust_bridge::for_generated::SseDeserializer::new(message);
+ let api_that = <RustOpaqueMoi<
+ flutter_rust_bridge::for_generated::RustAutoOpaqueInner<Operator>,
+ >>::sse_decode(&mut deserializer);
+ let api_path = <String>::sse_decode(&mut deserializer);
+ deserializer.end();
+ move |context| async move {
+ transform_result_sse::<_, flutter_rust_bridge::for_generated::anyhow::Error>(
+ (move || async move {
+ let mut api_that_guard = None;
+ let decode_indices_ =
+ flutter_rust_bridge::for_generated::lockable_compute_decode_order(
+ vec![flutter_rust_bridge::for_generated::LockableOrderInfo::new(
+ &api_that, 0, false,
+ )],
+ );
+ for i in decode_indices_ {
+ match i {
+ 0 => {
+ api_that_guard =
+ Some(api_that.lockable_decode_async_ref().await)
+ }
+ _ => unreachable!(),
+ }
+ }
+ let api_that_guard = api_that_guard.unwrap();
+ let output_ok =
+ crate::api::opendal_api::Operator::read(&*api_that_guard, api_path)
+ .await?;
+ Ok(output_ok)
+ })()
+ .await,
+ )
+ }
+ },
+ )
+}
+fn wire__crate__api__opendal_api__Operator_read_sync_impl(
+ ptr_: flutter_rust_bridge::for_generated::PlatformGeneralizedUint8ListPtr,
+ rust_vec_len_: i32,
+ data_len_: i32,
+) -> flutter_rust_bridge::for_generated::WireSyncRust2DartSse {
+ FLUTTER_RUST_BRIDGE_HANDLER.wrap_sync::<flutter_rust_bridge::for_generated::SseCodec, _>(
+ flutter_rust_bridge::for_generated::TaskInfo {
+ debug_name: "Operator_read_sync",
+ port: None,
+ mode: flutter_rust_bridge::for_generated::FfiCallMode::Sync,
+ },
+ move || {
+ let message = unsafe {
+ flutter_rust_bridge::for_generated::Dart2RustMessageSse::from_wire(
+ ptr_,
+ rust_vec_len_,
+ data_len_,
+ )
+ };
+ let mut deserializer =
+ flutter_rust_bridge::for_generated::SseDeserializer::new(message);
+ let api_that = <RustOpaqueMoi<
+ flutter_rust_bridge::for_generated::RustAutoOpaqueInner<Operator>,
+ >>::sse_decode(&mut deserializer);
+ let api_path = <String>::sse_decode(&mut deserializer);
+ deserializer.end();
+ transform_result_sse::<_, flutter_rust_bridge::for_generated::anyhow::Error>(
+ (move || {
+ let mut api_that_guard = None;
+ let decode_indices_ =
+ flutter_rust_bridge::for_generated::lockable_compute_decode_order(vec![
+ flutter_rust_bridge::for_generated::LockableOrderInfo::new(
+ &api_that, 0, false,
+ ),
+ ]);
+ for i in decode_indices_ {
+ match i {
+ 0 => api_that_guard = Some(api_that.lockable_decode_sync_ref()),
+ _ => unreachable!(),
+ }
+ }
+ let api_that_guard = api_that_guard.unwrap();
+ let output_ok =
+ crate::api::opendal_api::Operator::read_sync(&*api_that_guard, api_path)?;
+ Ok(output_ok)
+ })(),
+ )
+ },
+ )
+}
fn wire__crate__api__opendal_api__Operator_rename_impl(
port_: flutter_rust_bridge::for_generated::MessagePort,
ptr_: flutter_rust_bridge::for_generated::PlatformGeneralizedUint8ListPtr,
@@ -1075,6 +1183,122 @@ fn wire__crate__api__opendal_api__Operator_stat_sync_impl(
},
)
}
+fn wire__crate__api__opendal_api__Operator_write_impl(
+ port_: flutter_rust_bridge::for_generated::MessagePort,
+ ptr_: flutter_rust_bridge::for_generated::PlatformGeneralizedUint8ListPtr,
+ rust_vec_len_: i32,
+ data_len_: i32,
+) {
+ FLUTTER_RUST_BRIDGE_HANDLER.wrap_async::<flutter_rust_bridge::for_generated::SseCodec, _, _, _>(
+ flutter_rust_bridge::for_generated::TaskInfo {
+ debug_name: "Operator_write",
+ port: Some(port_),
+ mode: flutter_rust_bridge::for_generated::FfiCallMode::Normal,
+ },
+ move || {
+ let message = unsafe {
+ flutter_rust_bridge::for_generated::Dart2RustMessageSse::from_wire(
+ ptr_,
+ rust_vec_len_,
+ data_len_,
+ )
+ };
+ let mut deserializer =
+ flutter_rust_bridge::for_generated::SseDeserializer::new(message);
+ let api_that = <RustOpaqueMoi<
+ flutter_rust_bridge::for_generated::RustAutoOpaqueInner<Operator>,
+ >>::sse_decode(&mut deserializer);
+ let api_path = <String>::sse_decode(&mut deserializer);
+ let api_data = <Vec<u8>>::sse_decode(&mut deserializer);
+ deserializer.end();
+ move |context| async move {
+ transform_result_sse::<_, flutter_rust_bridge::for_generated::anyhow::Error>(
+ (move || async move {
+ let mut api_that_guard = None;
+ let decode_indices_ =
+ flutter_rust_bridge::for_generated::lockable_compute_decode_order(
+ vec![flutter_rust_bridge::for_generated::LockableOrderInfo::new(
+ &api_that, 0, false,
+ )],
+ );
+ for i in decode_indices_ {
+ match i {
+ 0 => {
+ api_that_guard =
+ Some(api_that.lockable_decode_async_ref().await)
+ }
+ _ => unreachable!(),
+ }
+ }
+ let api_that_guard = api_that_guard.unwrap();
+ let output_ok = crate::api::opendal_api::Operator::write(
+ &*api_that_guard,
+ api_path,
+ api_data,
+ )
+ .await?;
+ Ok(output_ok)
+ })()
+ .await,
+ )
+ }
+ },
+ )
+}
+fn wire__crate__api__opendal_api__Operator_write_sync_impl(
+ ptr_: flutter_rust_bridge::for_generated::PlatformGeneralizedUint8ListPtr,
+ rust_vec_len_: i32,
+ data_len_: i32,
+) -> flutter_rust_bridge::for_generated::WireSyncRust2DartSse {
+ FLUTTER_RUST_BRIDGE_HANDLER.wrap_sync::<flutter_rust_bridge::for_generated::SseCodec, _>(
+ flutter_rust_bridge::for_generated::TaskInfo {
+ debug_name: "Operator_write_sync",
+ port: None,
+ mode: flutter_rust_bridge::for_generated::FfiCallMode::Sync,
+ },
+ move || {
+ let message = unsafe {
+ flutter_rust_bridge::for_generated::Dart2RustMessageSse::from_wire(
+ ptr_,
+ rust_vec_len_,
+ data_len_,
+ )
+ };
+ let mut deserializer =
+ flutter_rust_bridge::for_generated::SseDeserializer::new(message);
+ let api_that = <RustOpaqueMoi<
+ flutter_rust_bridge::for_generated::RustAutoOpaqueInner<Operator>,
+ >>::sse_decode(&mut deserializer);
+ let api_path = <String>::sse_decode(&mut deserializer);
+ let api_data = <Vec<u8>>::sse_decode(&mut deserializer);
+ deserializer.end();
+ transform_result_sse::<_, flutter_rust_bridge::for_generated::anyhow::Error>(
+ (move || {
+ let mut api_that_guard = None;
+ let decode_indices_ =
+ flutter_rust_bridge::for_generated::lockable_compute_decode_order(vec![
+ flutter_rust_bridge::for_generated::LockableOrderInfo::new(
+ &api_that, 0, false,
+ ),
+ ]);
+ for i in decode_indices_ {
+ match i {
+ 0 => api_that_guard = Some(api_that.lockable_decode_sync_ref()),
+ _ => unreachable!(),
+ }
+ }
+ let api_that_guard = api_that_guard.unwrap();
+ let output_ok = crate::api::opendal_api::Operator::write_sync(
+ &*api_that_guard,
+ api_path,
+ api_data,
+ )?;
+ Ok(output_ok)
+ })(),
+ )
+ },
+ )
+}
// Section: related_funcs
@@ -1087,6 +1311,14 @@ flutter_rust_bridge::frb_generated_moi_arc_impl_value!(
// Section: dart2rust
+impl SseDecode for flutter_rust_bridge::for_generated::anyhow::Error {
+ // Codec=Sse (Serialization based), see doc to use other codecs
+ fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self {
+ let mut inner = <String>::sse_decode(deserializer);
+ return flutter_rust_bridge::for_generated::anyhow::anyhow!("{}", inner);
+ }
+}
+
impl SseDecode for Metadata {
// Codec=Sse (Serialization based), see doc to use other codecs
fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self {
@@ -1260,10 +1492,12 @@ fn pde_ffi_dispatcher_primary_impl(
14 => {
wire__crate__api__opendal_api__Operator_exists_impl(port, ptr, rust_vec_len, data_len)
}
- 17 => {
+ 17 => wire__crate__api__opendal_api__Operator_read_impl(port, ptr, rust_vec_len, data_len),
+ 19 => {
wire__crate__api__opendal_api__Operator_rename_impl(port, ptr, rust_vec_len, data_len)
}
- 19 => wire__crate__api__opendal_api__Operator_stat_impl(port, ptr, rust_vec_len, data_len),
+ 21 => wire__crate__api__opendal_api__Operator_stat_impl(port, ptr, rust_vec_len, data_len),
+ 23 => wire__crate__api__opendal_api__Operator_write_impl(port, ptr, rust_vec_len, data_len),
_ => unreachable!(),
}
}
@@ -1300,8 +1534,10 @@ fn pde_ffi_dispatcher_sync_impl(
13 => wire__crate__api__opendal_api__Operator_delete_sync_impl(ptr, rust_vec_len, data_len),
15 => wire__crate__api__opendal_api__Operator_exists_sync_impl(ptr, rust_vec_len, data_len),
16 => wire__crate__api__opendal_api__Operator_new_impl(ptr, rust_vec_len, data_len),
- 18 => wire__crate__api__opendal_api__Operator_rename_sync_impl(ptr, rust_vec_len, data_len),
- 20 => wire__crate__api__opendal_api__Operator_stat_sync_impl(ptr, rust_vec_len, data_len),
+ 18 => wire__crate__api__opendal_api__Operator_read_sync_impl(ptr, rust_vec_len, data_len),
+ 20 => wire__crate__api__opendal_api__Operator_rename_sync_impl(ptr, rust_vec_len, data_len),
+ 22 => wire__crate__api__opendal_api__Operator_stat_sync_impl(ptr, rust_vec_len, data_len),
+ 24 => wire__crate__api__opendal_api__Operator_write_sync_impl(ptr, rust_vec_len, data_len),
_ => unreachable!(),
}
}
@@ -1338,6 +1574,13 @@ impl flutter_rust_bridge::IntoIntoDart<FrbWrapper<Operator>> for Operator {
}
}
+impl SseEncode for flutter_rust_bridge::for_generated::anyhow::Error {
+ // Codec=Sse (Serialization based), see doc to use other codecs
+ fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) {
+ <String>::sse_encode(format!("{:?}", self), serializer);
+ }
+}
+
impl SseEncode for Metadata {
// Codec=Sse (Serialization based), see doc to use other codecs
fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) {
diff --git a/bindings/dart/tests/opendal_test.dart b/bindings/dart/tests/opendal_test.dart
index 693b08af1..516167ddb 100644
--- a/bindings/dart/tests/opendal_test.dart
+++ b/bindings/dart/tests/opendal_test.dart
@@ -1,4 +1,5 @@
import 'package:test/test.dart';
+import 'dart:typed_data';
import '../lib/opendal.dart';
void main() {
@@ -36,6 +37,39 @@ void main() {
expect(meta.isFile, false);
expect(meta.isDirectory, true);
});
+
+ test('File read/write round-trip', () async {
+ final storage = await Storage.init(schemeStr: "memory", map: {"root": "/tmp"});
+ final File = storage.initFile();
+ var testFile = File("roundtrip.txt");
+ final data = Uint8List.fromList([1, 2, 3, 4, 5]);
+
+ await testFile.write(data);
+ expect(await testFile.exists(), true);
+
+ final readData = await testFile.read();
+ expect(readData, equals(data));
+ });
+
+ test('File read from missing path throws', () async {
+ final storage = await Storage.init(schemeStr: "memory", map: {"root": "/tmp"});
+ final File = storage.initFile();
+ var missingFile = File("nonexistent.txt");
+
+ expect(() async => await missingFile.read(), throwsA(anything));
+ });
+
+ test('File overwrite semantics', () async {
+ final storage = await Storage.init(schemeStr: "memory", map: {"root": "/tmp"});
+ final File = storage.initFile();
+ var testFile = File("overwrite.txt");
+
+ await testFile.write(Uint8List.fromList([1, 2, 3]));
+ await testFile.write(Uint8List.fromList([4, 5]));
+
+ final readData = await testFile.read();
+ expect(readData, equals(Uint8List.fromList([4, 5])));
+ });
});
});
}