Skip to content

Commit

Permalink
resolving conflicts with main
Browse files Browse the repository at this point in the history
  • Loading branch information
Deep-co-de committed Jun 19, 2024
2 parents a94aa60 + 1a38f71 commit 79e1633
Show file tree
Hide file tree
Showing 53 changed files with 1,027 additions and 773 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/example_app.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ jobs:
uses: actions/setup-java@v4
with:
distribution: "zulu"
java-version: "11"
java-version: "17"

- name: Setup Python
uses: actions/setup-python@v5
Expand Down
21 changes: 15 additions & 6 deletions .github/workflows/quality_control.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ jobs:
code:
name: dart-and-rust-code
runs-on: ubuntu-latest
env:
RUSTFLAGS: -D warnings

steps:
- name: Checkout repository
Expand Down Expand Up @@ -52,14 +54,21 @@ jobs:
working-directory: flutter_ffi_plugin/example/
run: rinf message

- name: Check for any errors
# Targets are basically combinations of
# web/native and debug/release.
- name: Check for errors in various targets
run: |
rustup target add wasm32-unknown-unknown
RUSTFLAGS="-D warnings"
cargo check
cargo check --release
cargo check --target wasm32-unknown-unknown
cargo check --target wasm32-unknown-unknown --release
cargo clippy
cargo clippy --release
cargo clippy --target wasm32-unknown-unknown
cargo clippy --target wasm32-unknown-unknown --release
# The `--all-features` flag doesn't work for the entire workspace.
# That's why we are checking only the library crate.
- name: Check for errors with all features enabled
working-directory: rust_crate/
run: cargo clippy --all-features

- name: Analyze code
run: |
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/test_app.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ jobs:
uses: actions/setup-java@v4
with:
distribution: "zulu"
java-version: "11"
java-version: "17"

- name: Setup Python
uses: actions/setup-python@v5
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/user_app.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ jobs:
uses: actions/setup-java@v4
with:
distribution: "zulu"
java-version: "11"
java-version: "17"

- name: Setup Python
uses: actions/setup-python@v5
Expand Down
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,15 @@
## 6.12.1

- Fixed linefeed problem in published files.

## 6.12.0

- Generated message channels are now more efficient.
- Minimum Android SDK version increased from 16 to 21. Thanks @debanjanbasu!
- The `finalizeRust()` function in Dart has been removed. The `tokio` async runtime, which holds Rust logic, still drops automatically when the app ends.
- Bumped `prost` version to 0.12.6. Thanks @yeoupooh!
- Internal code has been organized.

## 6.11.1

- Fixed a bug with Dart's extension methods in the generated message code.
Expand Down
38 changes: 18 additions & 20 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,32 +32,30 @@ All platforms available with Flutter are [tested](https://github.com/cunarist/ri
Below is Dart code with widgets, and following that is Rust code with business logic.

```dart
MySchemaInput(
fieldOne: 1,
fieldTwo: 2,
fieldThree: 3,
).sendSignalToRust()
final stream = MySchemaOutput.rustSignalStream;
await for (final rustSignal in stream) {
// Custom Dart logic
}
StreamBuilder(
stream: MyMessage.rustSignalStream,
builder: (context, snapshot) {
final rustSignal = snapshot.data;
if (rustSignal == null) {
return Text("Nothing received yet");
}
final myMessage = rustSignal.message;
return Text(myMessage.currentNumber.toString());
},
),
```

```rust
MySchemaOutput {
field_four: 4,
field_five: 5,
field_six: 6,
}.send_signal_to_dart()

let mut receiver = MySchemaInput::get_dart_signal_receiver();
while let Some(dart_signal) = receiver.recv().await {
// Custom Rust logic
MyMessage {
current_number: 7,
other_bool: true,
}
.send_signal_to_dart();
```

All the message classes and structs are generated by Rinf. You can simply define the message schema with Protobuf. Sending messages between Dart and Rust is very convenient.
Of course, the opposite way from Dart to Rust is also possible in a similar manner.

All message classes and structs are generated by Rinf. You can define the message schema simply with Protobuf, making message passing between Dart and Rust very convenient.

## 🎁 Benefits

Expand Down
29 changes: 17 additions & 12 deletions automate/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,18 +55,18 @@ def replace_text_in_file(filepath: str, change_from: str, change_to: str):
)
replace_text_in_file(
"native/hub/src/lib.rs",
"// use tokio_with_wasm",
"use tokio_with_wasm",
"// use tokio_with_wasm::alias as tokio;",
"use tokio_with_wasm::alias as tokio;",
)
replace_text_in_file(
"native/hub/Cargo.toml",
"# wasm-bindgen",
"wasm-bindgen",
"# tokio_with_wasm",
"tokio_with_wasm",
)
replace_text_in_file(
"native/hub/Cargo.toml",
"# tokio_with_wasm",
"tokio_with_wasm",
"# wasm-bindgen",
"wasm-bindgen",
)

os.chdir("../")
Expand Down Expand Up @@ -109,18 +109,18 @@ def replace_text_in_file(filepath: str, change_from: str, change_to: str):
)
replace_text_in_file(
"native/hub/src/lib.rs",
"// use tokio_with_wasm",
"use tokio_with_wasm",
"// use tokio_with_wasm::alias as tokio;",
"use tokio_with_wasm::alias as tokio;",
)
replace_text_in_file(
"native/hub/Cargo.toml",
"# wasm-bindgen",
"wasm-bindgen",
"# tokio_with_wasm",
"tokio_with_wasm",
)
replace_text_in_file(
"native/hub/Cargo.toml",
"# tokio_with_wasm",
"tokio_with_wasm",
"# wasm-bindgen",
"wasm-bindgen",
)

os.chdir("../")
Expand All @@ -130,6 +130,11 @@ def replace_text_in_file(filepath: str, change_from: str, change_to: str):
"flutter_ffi_plugin/example/native/*",
"user_app/native/*",
)
replace_text_in_file(
"Cargo.toml",
'rinf = { path = "./rust_crate" }',
"",
)

elif sys.argv[1] == "prepare-example-app":
os.chdir("./flutter_ffi_plugin/example")
Expand Down
15 changes: 14 additions & 1 deletion documentation/docs/configuration.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# Configuration

You can customize some Rinf behaviors by configuring the `pubspec.yaml` file. Rinf will change its behaviors by reading the fields below. All fields are optional and it's not necessary to write them.
## 📋 YAML File

You can customize the behavior of Rinf CLI commands by configuring the `pubspec.yaml` file. All fields are optional and it's not necessary to write them.

```yaml title="pubspec.yaml"
rinf:
Expand All @@ -16,3 +18,14 @@ You can check the current configuration status by running the command below in t
```bash title="CLI"
rinf config
```

## 📦 Crate Features

Customizing the behavior of the Rinf crate is possible through its crate features.

```toml title="native/hub/Cargo.toml"
rinf = { version = "0.0.0", features = ["feature-name"] }
```

- `multi-worker`: Starts a worker thread for each CPU core available on the system within the `tokio` runtime by enabling its `rt-multi-thread` feature. By default, the `tokio` runtime uses only one thread. Enabling this feature allows the `tokio` runtime to utilize all the cores on your computer. This feature does not affect applications on the web platform.
- `show-backtrace`: Prints the full backtrace in the CLI when a panic occurs in debug mode. In general, backtrace is not very helpful when debugging async apps, so consider using [`tracing`](https://crates.io/crates/tracing) for logging purposes. Note that this feature does not affect debugging on the web platform.
32 changes: 12 additions & 20 deletions documentation/docs/frequently-asked-questions.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ android {

### How does concurrency work under the hood?

On native platforms, Dart runs in a single thread as usual, while Rust utilizes the async `tokio` runtime to take advantage of all cores on the computer, allowing async tasks to run efficiently within that runtime.
On native platforms, Dart runs in the main thread, while Rust utilizes the async `tokio` runtime, allowing async tasks to run efficiently within a separate thread.

On the web, Dart and Rust both run inside JavaScript's async event loop in the main thread, with Rust `Future`s being converted into JavaScript `Promise`s internally. This is a necessary constraint because [webassembly component proposal](https://github.com/WebAssembly/proposals) is not stabilized as of February 2024.

Expand Down Expand Up @@ -131,9 +131,9 @@ There might be various Rust codes with these attribute above:

```rust title="Rust"
#[cfg(target_family = "wasm")]
...
{}
#[cfg(not(target_family = "wasm"))]
...
{}
```

Since the environments of the web and native platforms are so different, there are times when you need to use these attributes to include and exclude parts of the code depending on whether they are targeting web or not.
Expand All @@ -148,6 +148,8 @@ By default, Rust-analyzer runs in native mode. To make it run in webassembly mod
target = "wasm32-unknown-unknown"
```

You need to restart Rust language server for this to take effect.

### CMake cache is broken after I moved the app folder

```title="Output"
Expand All @@ -171,12 +173,9 @@ If you are using older Android versions, you may encounter errors due to issues
To address this, you can modify `AndroidManifest.xml` files under `./android/app/src/` as follows.

```xml title="android/app/src/**/AndroidManifest.xml"
...
<application
android:extractNativeLibs="true"
...
>
...
```

### How can I await a response?
Expand All @@ -188,7 +187,7 @@ However, if you really need to store some state in a Flutter widget, you can ach
```proto title="messages/tutorial_resource.proto"
syntax = "proto3";
package tutorial_resource;
...
// [RINF:DART-SIGNAL]
message MyUniqueInput {
int32 interaction_id = 1;
Expand All @@ -203,29 +202,24 @@ message MyUniqueOutput {
```

```dart title="lib/main.dart"
...
import 'dart:async';
import 'package:example_app/messages/tutorial_resource.pb.dart';
var currentInteractionId = 0;
final myUniqueOutputs = Map<int, Completer<MyUniqueOutput>>();
void main() async {
...
MyUniqueOutput.rustSignalStream.listen((rustSignal) {
final myUniqueInput = rustSignal.message;
myUniqueOutputs[myUniqueInput.interactionId]!.complete(myUniqueInput);
});
...
}
...
```

```dart title="lib/main.dart"
...
import 'dart:async';
import 'package:example_app/messages/tutorial_resource.pb.dart';
...
onPressed: () async {
final completer = Completer<MyUniqueOutput>();
myUniqueOutputs[currentInteractionId] = completer;
Expand All @@ -236,14 +230,13 @@ onPressed: () async {
currentInteractionId += 1;
final myUniqueOutput = await completer.future;
},
...
```

```rust title="native/hub/src/sample_functions.rs"
pub async fn respond() {
pub async fn respond() -> Result<()> {
use messages::tutorial_resource::*;
let mut receiver = MyUniqueInput::get_dart_signal_receiver();
let mut receiver = MyUniqueInput::get_dart_signal_receiver()?;
while let Some(dart_signal) = receiver.recv().await {
let my_unique_input = dart_signal.message;
MyUniqueOutput {
Expand All @@ -252,12 +245,13 @@ pub async fn respond() {
}
.send_signal_to_dart();
}
Ok(())
}
```

```rust title="native/hub/src/lib.rs"
async fn main() {
...
tokio::spawn(sample_functions::respond());
}
```
Expand All @@ -270,7 +264,7 @@ Here are the current constraints of the `wasm32-unknown-unknown` target:

- Numerous functionalities within `std::fs` remain unimplemented.
- Various features of `std::net` are not available. Consider using `reqwest` crate instead. `reqwest` supports `wasm32-unknown-unknown` and relies on JavaScript to perform network communications.
- `std::thread::spawn` doesn't work. Consider using `tokio_with_wasm::tokio::task::spawn_blocking` instead.
- `std::thread::spawn` doesn't work. Consider using `tokio_with_wasm::task::spawn_blocking` instead.
- Several features of `std::time::Instant` are unimplemented. Consider using `chrono` as an alternative. `chrono` supports `wasm32-unknown-unknown` and relies on JavaScript to obtain system time.
- In case of a panic in an asynchronous Rust task, it aborts and throws a JavaScript `RuntimeError` [which Rust cannot catch](https://stackoverflow.com/questions/59426545/rust-paniccatch-unwind-no-use-in-webassembly). A recommended practice is to replace `.unwrap` with `.expect` or handle errors with `Err` instances.

Expand Down Expand Up @@ -324,9 +318,7 @@ import './messages/generated.dart';
async void main() {
await initializeRust(compiledLibPath: "/path/to/library/libhub.so");
...
}
...
```

This provided path will be used for finding dynamic library files on native platforms with Dart's `DynamicLibrary.open([compiledLibPath])`, and for loading the JavaScript module on the web with `import init, * as wasmBindings from "[compiledLibPath]"`.
17 changes: 11 additions & 6 deletions documentation/docs/graceful-shutdown.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
# Graceful Shutdown

When the Flutter app is closed, the entire `tokio` runtime on the Rust side will be terminated automatically. However, you might need to run some finalization code in Rust before the app closes. This might involve saving files or disposing of resources. To achieve this, you can call `finalizeRust()` in Dart to terminate all Rust tasks before closing the Flutter app.
When the Flutter app is closed, the entire `tokio` async runtime on the Rust side will be terminated automatically. Even if the app is force-closed, the `tokio` async runtime will be properly dropped.

When using Rinf, the lifetime of the `tokio` runtime follows that of the Dart runtime. This behavior is different from typical `tokio` executables where its async runtime lives throughout the async `main()` function of Rust.

In some cases, you might need to run some finalization code in Rust before the app closes. This might involve saving files or disposing of resources. To achieve this, you can use Flutter's `AppLifecycleListener` to run something or to get user confirmation before closing the Flutter app.

```dart title="lib/main.dart"
import 'dart:ui';
import 'package:flutter/material.dart';
import './messages/generated.dart';
...
class MyApp extends StatefulWidget {
const MyApp({super.key});
Expand All @@ -17,8 +20,7 @@ class MyApp extends StatefulWidget {
class _MyAppState extends State<MyApp> {
final _appLifecycleListener = AppLifecycleListener(
onExitRequested: () async {
// Terminate Rust tasks before closing the Flutter app.
await finalizeRust();
// Do something here before the app is exited.
return AppExitResponse.exit;
},
);
Expand All @@ -37,5 +39,8 @@ class _MyAppState extends State<MyApp> {
);
}
}
...
```

It's worth noting that `AppLifecycleListener` or `dispose` cannot always be relied upon for app closings. Below is a text snippet quoted from the official [Flutter docs](https://api.flutter.dev/flutter/widgets/State/dispose.html):

> There is no way to predict when application shutdown will happen. For example, a user's battery could catch fire, or the user could drop the device into a swimming pool, or the operating system could unilaterally terminate the application process due to memory pressure. Applications are responsible for ensuring they behave well even in the face of rapid, unscheduled termination.
Loading

0 comments on commit 79e1633

Please sign in to comment.