Skip to content

Commit 48f1441

Browse files
feat(bindings/dart): Add dart binding (#5591)
* init * add license head * now the draft is done * add Capability * add init example * add readme && gitignore * use script to build * remove uses * add RUST_LOG * fix opendal path * fix name * now it can go to build, use tmate * try fix compile error * tmp remove Operator * fix version * add gitignore * add some api && add code gen result * add test * rm js comment * rm gen * do some fix * back to release build * test file * fix step 1 * try fix * private cap * gen * rm cap * fmt * add example in readme * use dart stdlib style * 2.8.0 * dart dep version * use Manager to give class
1 parent 94e7896 commit 48f1441

23 files changed

+4378
-0
lines changed

.gitattributes

+1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ integrations export-ignore
99
bindings/c export-ignore
1010
bindings/cpp export-ignore
1111
bindings/d export-ignore
12+
bindings/dart export-ignore
1213
bindings/dotnet export-ignore
1314
bindings/go export-ignore
1415
bindings/haskell export-ignore
+86
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
# Licensed to the Apache Software Foundation (ASF) under one
2+
# or more contributor license agreements. See the NOTICE file
3+
# distributed with this work for additional information
4+
# regarding copyright ownership. The ASF licenses this file
5+
# to you under the Apache License, Version 2.0 (the
6+
# "License"); you may not use this file except in compliance
7+
# with the License. You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing,
12+
# software distributed under the License is distributed on an
13+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
# KIND, either express or implied. See the License for the
15+
# specific language governing permissions and limitations
16+
# under the License.
17+
18+
name: Bindings Dart CI
19+
20+
on:
21+
push:
22+
branches:
23+
- main
24+
tags:
25+
- "*"
26+
pull_request:
27+
branches:
28+
- main
29+
paths:
30+
- "core/**"
31+
- "bindings/dart/**"
32+
- ".github/workflows/ci_bindings_dart.yml"
33+
workflow_dispatch:
34+
35+
env:
36+
FLUTTER_VERSION: "3.27.3"
37+
38+
concurrency:
39+
group: ${{ github.workflow }}-${{ github.ref }}-${{ github.event_name }}
40+
cancel-in-progress: true
41+
42+
permissions:
43+
contents: read
44+
45+
jobs:
46+
test:
47+
runs-on: ubuntu-latest
48+
steps:
49+
- uses: actions/checkout@v4
50+
51+
- name: Setup Rust toolchain
52+
uses: ./.github/actions/setup
53+
54+
- name: Install Flutter
55+
uses: subosito/flutter-action@v2
56+
with:
57+
flutter-version: ${{ env.FLUTTER_VERSION }}
58+
channel: 'stable'
59+
60+
- name: Install flutter_rust_bridge_codegen and cargo-expand and fvm
61+
working-directory: bindings/dart
62+
run: |
63+
set -eux -o pipefail
64+
# cargo install cargo-expand
65+
# cargo install flutter_rust_bridge_codegen --version "2.7.1"
66+
67+
- name: Check generated bridge code is sync
68+
working-directory: bindings/dart
69+
run: |
70+
set -eux -o pipefail
71+
git diff
72+
73+
- name: Flutter pub get
74+
working-directory: bindings/dart
75+
run: flutter pub get
76+
77+
- name: build lib
78+
working-directory: bindings/dart/rust
79+
run: |
80+
set -eux -o pipefail
81+
cargo build -r # frb will load the so in release/, so release build here
82+
83+
- name: test
84+
working-directory: bindings/dart
85+
run: |
86+
dart run lib/opendal_test.dart

README.md

+2
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ OpenDAL's development is guided by its vision of **One Layer, All Storage** and
1717
| [C Binding] | - | [![Docs Dev]][C Binding Dev Docs] |
1818
| [Cpp Binding] | - | [![Docs Dev]][Cpp Binding Dev Docs] |
1919
| [D Binding] | - | - |
20+
| [Dart Binding] | - | - |
2021
| [Dotnet Binding] | - | - |
2122
| [Go Binding] | [![Go Binding Image]][Go Binding Link] | [![Docs Release]][Go Release Docs] |
2223
| [Haskell Binding] | - | - |
@@ -42,6 +43,7 @@ OpenDAL's development is guided by its vision of **One Layer, All Storage** and
4243
[Cpp Binding]: bindings/cpp/README.md
4344
[Cpp Binding Dev Docs]: https://opendal.apache.org/docs/cpp/
4445
[D Binding]: bindings/d/README.md
46+
[Dart Binding]: bindings/dart/README.md
4547
[Dotnet Binding]: bindings/dotnet/README.md
4648
[Go Binding]: bindings/go/README.md
4749
[Go Binding Image]: https://badge.fury.io/go/github.com%2Fapache%2Fopendal%2Fbindings%2Fgo.svg

bindings/README.md

+1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ This folder contains the bindings for OpenDAL. Currently, we support the followi
1414
* [C++](cpp/README.md)
1515
* [C#](dotnet/README.md)
1616
* [D](d/README.md)
17+
* [Dart](dart/README.md)
1718
* [Go](go/README.md)
1819
* [Haskell](haskell/README.md)
1920
* [Lua](lua/README.md)

bindings/dart/.gitignore

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# See https://www.dartlang.org/guides/libraries/private-files
2+
3+
# Files and directories created by pub
4+
.dart_tool/
5+
.packages
6+
build/
7+
# If you're building an application, you may want to check-in your pubspec.lock
8+
pubspec.lock
9+
10+
# Directory created by dartdoc
11+
# If you don't generate documentation locally you can remove this line.
12+
doc/api/
13+
14+
# dotenv environment variables file
15+
.env*
16+
17+
# Avoid committing generated Javascript files:
18+
*.dart.js
19+
*.info.json # Produced by the --dump-info flag.
20+
*.js # When generated by dart2js. Don't specify *.js if your
21+
# project includes source files written in JavaScript.
22+
*.js_
23+
*.js.deps
24+
*.js.map
25+
26+
.flutter-plugins
27+
.flutter-plugins-dependencies

bindings/dart/README.md

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# Apache OpenDAL™ Dart Binding (WIP)
2+
3+
## Development
4+
5+
```
6+
flutter pub get
7+
flutter_rust_bridge_codegen generate
8+
cd rust
9+
cargo build -r
10+
cd ..
11+
dart run lib/opendal_test.dart
12+
```
13+
14+
## License and Trademarks
15+
16+
Licensed under the Apache License, Version 2.0: http://www.apache.org/licenses/LICENSE-2.0
17+
18+
Apache OpenDAL, OpenDAL, and Apache are either registered trademarks or trademarks of the Apache Software Foundation.

bindings/dart/analysis_options.yaml

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# Licensed to the Apache Software Foundation (ASF) under one
2+
# or more contributor license agreements. See the NOTICE file
3+
# distributed with this work for additional information
4+
# regarding copyright ownership. The ASF licenses this file
5+
# to you under the Apache License, Version 2.0 (the
6+
# "License"); you may not use this file except in compliance
7+
# with the License. You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing,
12+
# software distributed under the License is distributed on an
13+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
# KIND, either express or implied. See the License for the
15+
# specific language governing permissions and limitations
16+
# under the License.
17+
18+
analyzer:
19+
exclude:
20+
- rust/target/**.dart # contains dumped debug info, instead of normal code
+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# Licensed to the Apache Software Foundation (ASF) under one
2+
# or more contributor license agreements. See the NOTICE file
3+
# distributed with this work for additional information
4+
# regarding copyright ownership. The ASF licenses this file
5+
# to you under the Apache License, Version 2.0 (the
6+
# "License"); you may not use this file except in compliance
7+
# with the License. You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing,
12+
# software distributed under the License is distributed on an
13+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
# KIND, either express or implied. See the License for the
15+
# specific language governing permissions and limitations
16+
# under the License.
17+
18+
rust_input: crate::api
19+
rust_root: rust/
20+
dart_output: lib/src/rust
21+
local: true

bindings/dart/lib/opendal.dart

+120
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
import 'src/rust/frb_generated.dart';
2+
import 'src/rust/api/opendal_api.dart';
3+
4+
class FileManager {
5+
final Operator _operator;
6+
7+
FileManager._(this._operator);
8+
9+
static FileManager initOp(
10+
{required String schemeStr, required Map<String, String> map}) {
11+
return FileManager._(Operator(schemeStr: schemeStr, map: map));
12+
}
13+
14+
File call(String path) {
15+
return File._(path: path, operator: _operator);
16+
}
17+
}
18+
19+
class DirectoryManager {
20+
final Operator _operator;
21+
22+
DirectoryManager._(this._operator);
23+
24+
static DirectoryManager initOp(
25+
{required String schemeStr, required Map<String, String> map}) {
26+
return DirectoryManager._(Operator(schemeStr: schemeStr, map: map));
27+
}
28+
29+
Directory call(String path) {
30+
return Directory._(path: path, operator: _operator);
31+
}
32+
}
33+
34+
class File {
35+
final String path;
36+
final Operator _operator;
37+
38+
File._({required this.path, required Operator operator})
39+
: _operator = operator;
40+
41+
Future<bool> exists() {
42+
return _operator.isExist(path: path);
43+
}
44+
45+
bool existsSync() {
46+
return _operator.isExistSync(path: path);
47+
}
48+
49+
Future<Metadata> stat() {
50+
return _operator.stat(path: path);
51+
}
52+
53+
Metadata statSync() {
54+
return _operator.statSync(path: path);
55+
}
56+
57+
Future<void> delete() {
58+
return _operator.delete(path: path);
59+
}
60+
61+
Future<void> rename(String newPath) {
62+
return _operator.rename(from: path, to: newPath);
63+
}
64+
65+
void renameSync(String newPath) {
66+
_operator.renameSync(from: path, to: newPath);
67+
}
68+
69+
void deleteSync() {
70+
_operator.deleteSync(path: path);
71+
}
72+
}
73+
74+
class Directory {
75+
final String path;
76+
final Operator _operator;
77+
78+
Directory._({required this.path, required Operator operator})
79+
: _operator = operator;
80+
81+
Future<void> create() {
82+
return _operator.createDir(path: path);
83+
}
84+
85+
void createSync() {
86+
_operator.createDirSync(path: path);
87+
}
88+
89+
Future<bool> exists() {
90+
return _operator.isExist(path: path);
91+
}
92+
93+
bool existsSync() {
94+
return _operator.isExistSync(path: path);
95+
}
96+
97+
Future<void> rename(String newPath) {
98+
return _operator.rename(from: path, to: newPath);
99+
}
100+
101+
void renameSync(String newPath) {
102+
_operator.renameSync(from: path, to: newPath);
103+
}
104+
105+
Future<Metadata> stat() {
106+
return _operator.stat(path: path);
107+
}
108+
109+
Metadata statSync() {
110+
return _operator.statSync(path: path);
111+
}
112+
113+
Future<void> delete() {
114+
return _operator.delete(path: path);
115+
}
116+
117+
void deleteSync() {
118+
_operator.deleteSync(path: path);
119+
}
120+
}

bindings/dart/lib/opendal_test.dart

+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import 'package:test/test.dart';
2+
import 'src/rust/frb_generated.dart';
3+
import 'src/rust/api/opendal_api.dart';
4+
import 'opendal.dart';
5+
6+
void main() {
7+
group('opendal unit test', () {
8+
group('opendal fs schema', () {
9+
test('File and Directory functions in fs schema', () async {
10+
await RustLib.init();
11+
final File = FileManager.initOp(schemeStr: "fs", map: {"root": "/tmp"});
12+
var testFile = File("test_1.txt");
13+
expect(await testFile.exists(), false);
14+
15+
var anotherFile = File("test.txt");
16+
expect(await anotherFile.exists(), false);
17+
18+
final Directory =
19+
DirectoryManager.initOp(schemeStr: "fs", map: {"root": "/tmp"});
20+
var testDir = Directory("test_dir/");
21+
await testDir.create();
22+
expect(await testDir.exists(), true);
23+
});
24+
});
25+
26+
group('opendal memory schema', () {
27+
test('File and Directory functions in memory schema', () async {
28+
final File =
29+
FileManager.initOp(schemeStr: "memory", map: {"root": "/tmp"});
30+
final Directory =
31+
DirectoryManager.initOp(schemeStr: "memory", map: {"root": "/tmp"});
32+
var testDir = Directory("test/");
33+
await testDir.create();
34+
expect(
35+
await testDir.exists(), isTrue); // Directory exists after creation
36+
37+
final meta = await testDir.stat();
38+
expect(meta, isNotNull);
39+
expect(meta.isFile, false);
40+
expect(meta.isDirectory, true);
41+
});
42+
});
43+
});
44+
}

0 commit comments

Comments
 (0)