Skip to content

Commit

Permalink
Merge pull request #405 from H2Sxxa/main
Browse files Browse the repository at this point in the history
feat: Support for marking "RUST-ATTRIBUTE" (type attribute)
  • Loading branch information
temeddix authored Jul 27, 2024
2 parents 9edc426 + d90b655 commit 076feb6
Show file tree
Hide file tree
Showing 4 changed files with 56 additions and 23 deletions.
37 changes: 22 additions & 15 deletions documentation/docs/messaging.md
Original file line number Diff line number Diff line change
@@ -1,16 +1,20 @@
# Messaging

There are 2 types of special comments that you can mark messages with.
There are special comments that you can mark messages with.

## 📢 Rust Signal
## 📢 Channels

`[RINF:RUST-SIGNAL]` generates a channel from Rust to Dart.
`[RINF:RUST-SIGNAL]` generates a message channel from Rust to Dart.

```proto title="Protobuf"
// [RINF:RUST-SIGNAL]
message MyDataOutput { ... }
```

```rust title="Rust"
MyDataOutput { ... }.send_signal_to_dart();
```

```dart title="Dart"
StreamBuilder(
stream: MyDataOutput.rustSignalStream,
Expand All @@ -25,17 +29,18 @@ StreamBuilder(
)
```

```rust title="Rust"
MyDataOutput { ... }.send_signal_to_dart();
```

Use `[RINF:RUST-SIGNAL-BINARY]` to include binary data without the overhead of serialization.

```proto title="Protobuf"
// [RINF:RUST-SIGNAL-BINARY]
message MyDataOutput { ... }
```

```rust title="Rust"
let binary: Vec<u8> = vec![0; 64];
MyDataOutput { ... }.send_signal_to_dart(binary);
```

```dart title="Dart"
StreamBuilder(
stream: MyDataOutput.rustSignalStream,
Expand All @@ -51,14 +56,7 @@ StreamBuilder(
)
```

```rust title="Rust"
let binary: Vec<u8> = vec![0; 64];
MyDataOutput { ... }.send_signal_to_dart(binary);
```

## 📭 Dart Signal

`[RINF:DART-SIGNAL]` generates a channel from Dart to Rust.
`[RINF:DART-SIGNAL]` generates a message channel from Dart to Rust.

```proto title="Protobuf"
// [RINF:DART-SIGNAL]
Expand Down Expand Up @@ -97,3 +95,12 @@ while let Some(dart_signal) = receiver.recv().await {
// Custom Rust logic here
}
```

## 🔖 Attributes

`[RINF:RUST-ATTRIBUTE(...)]` writes an attribute above the generated message struct in Rust. This is useful when you want to automatically implement a trait for the message struct in Rust.

```proto title="Protobuf"
// [RINF:RUST-ATTRIBUTE(#[derive(Copy)])]
message MyDataInput { ... }
```
33 changes: 27 additions & 6 deletions flutter_ffi_plugin/bin/src/message.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ enum MarkType {
dartSignalBinary,
rustSignal,
rustSignalBinary,
rustAttribute,
}

class MarkedMessage {
Expand Down Expand Up @@ -47,6 +48,12 @@ Future<void> generateMessageCode({
resourcesInFolders,
);

// Analyze marked messages in `.proto` files.
final markedMessagesAll = await analyzeMarkedMessages(
protoPath,
resourcesInFolders,
);

// Include `package` statement in `.proto` files.
// Package name should be the same as the filename
// because Rust filenames are written with package name
Expand Down Expand Up @@ -115,6 +122,12 @@ Future<void> generateMessageCode({
'--prost_out=$rustFullPath',
...(messageConfig.rustSerde ? ['--prost-serde_out=$rustFullPath'] : []),
...resourceNames.map((name) => '$name.proto'),
...markedMessagesAll.values.fold<List<String>>([], (args, messages) {
messages.values.forEach((messages) => args.addAll(messages
.where((message) => message.markType == MarkType.rustAttribute)
.map((message) => message.name)));
return args;
})
]);
if (protocRustResult.exitCode != 0) {
print(protocRustResult.stderr.toString().trim());
Expand Down Expand Up @@ -219,12 +232,6 @@ Future<void> generateMessageCode({
}
}

// Analyze marked messages in `.proto` files.
final markedMessagesAll = await analyzeMarkedMessages(
protoPath,
resourcesInFolders,
);

// Prepare communication channels between Dart and Rust.
for (final entry in markedMessagesAll.entries) {
final subPath = entry.key;
Expand Down Expand Up @@ -761,6 +768,8 @@ Future<Map<String, Map<String, List<MarkedMessage>>>> analyzeMarkedMessages(
);
final content = await protoFile.readAsString();
final regExp = RegExp(r'{[^}]*}');
final attrExp = RegExp(r"(?<=\[RINF:RUST-ATTRIBUTE\().*(?=\)\])");

// Remove all { ... } blocks from the string
final contentWithoutBlocks = content.replaceAll(regExp, ';');
final statements = contentWithoutBlocks.split(";");
Expand Down Expand Up @@ -790,6 +799,18 @@ Future<Map<String, Map<String, List<MarkedMessage>>>> analyzeMarkedMessages(
} else if (statement.contains("[RINF:RUST-SIGNAL-BINARY]")) {
markType = MarkType.rustSignalBinary;
}

// find [RINF:RUST-ATTRIBUTE(...)]
var attr = attrExp.stringMatch(statement);
if (attr != null) {
markedMessages[subPath]![filename]!.add(MarkedMessage(
MarkType.rustAttribute,
"--prost_opt=type_attribute=$filename.$messageName=${attr.replaceAll(",", "\\,")}",
-1,
));
continue;
}

if (markType == null) {
// If there's no mark in the message, just ignore it
continue;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
syntax = "proto3";
package enum_and_oneof;
package sample_file;

enum Kind {
one = 0;
Expand All @@ -23,3 +23,8 @@ message SampleOutput {
int32 age = 3;
}
}

// [RINF:RUST-ATTRIBUTE(#[derive(Copy)])]
message WithRustAttribute {
bool dummy = 1;
}
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ pub async fn stream_fractal() {
// A dummy function that uses sample messages to eliminate warnings.
#[allow(dead_code)]
async fn use_messages() -> Result<()> {
use messages::sample_folder::enum_and_oneof::*;
use messages::sample_folder::sample_file::*;
let _ = SampleInput::get_dart_signal_receiver()?;
SampleOutput {
kind: 3,
Expand Down

0 comments on commit 076feb6

Please sign in to comment.