Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Return RinfError on errors #381

Merged
merged 16 commits into from
Jun 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion .github/workflows/quality_control.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -54,14 +54,22 @@ 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
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: |
dart analyze flutter_ffi_plugin --fatal-infos
Expand Down
68 changes: 30 additions & 38 deletions flutter_ffi_plugin/bin/src/message.dart
Original file line number Diff line number Diff line change
Expand Up @@ -258,16 +258,13 @@ import 'package:rinf/rinf.dart';
rustPath,
'''
#![allow(unused_imports)]
#![allow(dead_code)]

use crate::tokio;
use prost::Message;
use rinf::{send_rust_signal, DartSignal};
use std::error::Error;
use rinf::{debug_print, send_rust_signal, DartSignal, RinfError};
use std::sync::Mutex;
use tokio::sync::mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender};

type Result<T> = std::result::Result<T, Box<dyn Error + Send + Sync>>;
''',
atFront: true,
);
Expand All @@ -292,11 +289,11 @@ pub static ${snakeName.toUpperCase()}_CHANNEL: ${messageName}Cell =

impl ${normalizePascal(messageName)} {
pub fn get_dart_signal_receiver()
-> Result<UnboundedReceiver<DartSignal<Self>>>
-> Result<UnboundedReceiver<DartSignal<Self>>, RinfError>
{
let mut guard = ${snakeName.toUpperCase()}_CHANNEL
.lock()
.map_err(|_| "Could not acquire the channel lock.")?;
.map_err(|_| RinfError::LockMessageChannel)?;
if guard.is_none() {
let (sender, receiver) = unbounded_channel();
guard.replace((sender, Some(receiver)));
Expand All @@ -308,19 +305,19 @@ impl ${normalizePascal(messageName)} {
// which is now closed.
let pair = guard
.as_ref()
.ok_or("Message channel in Rust not present.")?;
.ok_or(RinfError::NoMessageChannel)?;
if pair.0.is_closed() {
let (sender, receiver) = unbounded_channel();
guard.replace((sender, Some(receiver)));
}
}
let pair = guard
.take()
.ok_or("Message channel in Rust not present.")?;
.ok_or(RinfError::NoMessageChannel)?;
guard.replace((pair.0, None));
let receiver = pair
.1
.ok_or("Each Dart signal receiver can be taken only once.")?;
.ok_or(RinfError::MessageReceiverTaken)?;
Ok(receiver)
}
}
Expand Down Expand Up @@ -382,11 +379,14 @@ final ${camelName}Controller = StreamController<RustSignal<$messageName>>();
'''
impl ${normalizePascal(messageName)} {
pub fn send_signal_to_dart(&self) {
send_rust_signal(
let result = send_rust_signal(
${markedMessage.id},
self.encode_to_vec(),
Vec::new(),
);
if let Err(error) = result {
debug_print!("{error}\\n{self:?}");
}
}
}
''',
Expand All @@ -398,11 +398,14 @@ impl ${normalizePascal(messageName)} {
'''
impl ${normalizePascal(messageName)} {
pub fn send_signal_to_dart(&self, binary: Vec<u8>) {
send_rust_signal(
let result = send_rust_signal(
${markedMessage.id},
self.encode_to_vec(),
binary,
);
if let Err(error) = result {
debug_print!("{error}\\n{self:?}");
}
}
}
''',
Expand All @@ -420,30 +423,23 @@ impl ${normalizePascal(messageName)} {

use crate::tokio;
use prost::Message;
use rinf::debug_print;
use rinf::DartSignal;
use rinf::{debug_print, DartSignal, RinfError};
use std::collections::HashMap;
use std::error::Error;
use std::sync::OnceLock;
use tokio::sync::mpsc::unbounded_channel;

type SignalHandlers = OnceLock<
HashMap<i32, Box<dyn Fn(&[u8], &[u8])
-> Result<(), Box<dyn Error>> + Send + Sync>>,
>;
static SIGNAL_HANDLERS: SignalHandlers = OnceLock::new();
type Handler = dyn Fn(&[u8], &[u8]) -> Result<(), RinfError> + Send + Sync;
type SignalHandlers = HashMap<i32, Box<Handler>>;
static SIGNAL_HANDLERS: OnceLock<SignalHandlers> = OnceLock::new();

pub fn handle_dart_signal(
message_id: i32,
message_bytes: &[u8],
binary: &[u8]
) {
) -> Result<(), RinfError> {
let hash_map = SIGNAL_HANDLERS.get_or_init(|| {
let mut new_hash_map = HashMap::<
i32,
Box<dyn Fn(&[u8], &[u8])
-> Result<(), Box<dyn Error>> + Send + Sync>,
>::new();
let mut new_hash_map: SignalHandlers = HashMap::new();
''';
for (final entry in markedMessagesAll.entries) {
final subpath = entry.key;
Expand All @@ -464,14 +460,16 @@ new_hash_map.insert(
${markedMessage.id},
Box::new(|message_bytes: &[u8], binary: &[u8]| {
use super::$modulePath$filename::*;
let message = ${normalizePascal(messageName)}::decode(
message_bytes
)?;
let message =
${normalizePascal(messageName)}::decode(message_bytes)
.map_err(|_| RinfError::DecodeMessage)?;
let dart_signal = DartSignal {
message,
binary: binary.to_vec(),
};
let mut guard = ${snakeName.toUpperCase()}_CHANNEL.lock()?;
let mut guard = ${snakeName.toUpperCase()}_CHANNEL
.lock()
.map_err(|_| RinfError::LockMessageChannel)?;
if guard.is_none() {
let (sender, receiver) = unbounded_channel();
guard.replace((sender, Some(receiver)));
Expand All @@ -483,15 +481,15 @@ new_hash_map.insert(
// which is now closed.
let pair = guard
.as_ref()
.ok_or("Message channel in Rust not present.")?;
.ok_or(RinfError::NoMessageChannel)?;
if pair.0.is_closed() {
let (sender, receiver) = unbounded_channel();
guard.replace((sender, Some(receiver)));
}
}
let pair = guard
.as_ref()
.ok_or("Message channel in Rust not present.")?;
.ok_or(RinfError::NoMessageChannel)?;
let sender = &pair.0;
let _ = sender.send(dart_signal);
Ok(())
Expand All @@ -508,15 +506,9 @@ new_hash_map.insert(

let signal_handler = match hash_map.get(&message_id) {
Some(inner) => inner,
None => {
debug_print!("Message ID not found in the handler Hashmap.");
return;
}
None => return Err(RinfError::NoSignalHandler),
};
let result = signal_handler(message_bytes, binary);
if let Err(error) = result {
debug_print!("Could not process hashmap.\\n{error:#?}");
}
signal_handler(message_bytes, binary)
}
''';
await File.fromUri(rustOutputPath.join('generated.rs'))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ pub async fn stream_fractal() {
Ok(inner) => inner,
Err(_) => continue,
};
if let Some(fractal_image) = received_frame {
if let Ok(fractal_image) = received_frame {
// Stream the image data to Dart.
SampleFractal {
current_scale,
Expand Down
3 changes: 0 additions & 3 deletions flutter_ffi_plugin/example/native/sample_crate/src/common.rs

This file was deleted.

18 changes: 18 additions & 0 deletions flutter_ffi_plugin/example/native/sample_crate/src/error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
use std::error::Error;
use std::fmt;

#[derive(Debug)]
pub struct ExampleError(pub Box<dyn Error + Send + Sync>);

impl fmt::Display for ExampleError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let source = self.0.as_ref();
write!(f, "An error occured inside the example code.\n{source}")
}
}

impl Error for ExampleError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
Some(self.0.as_ref())
}
}
7 changes: 4 additions & 3 deletions flutter_ffi_plugin/example/native/sample_crate/src/fractal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
//! Copied and modified from
//! https://github.com/abour/fractal repository.

use crate::error::ExampleError;
use image::ImageEncoder;

const WIDTH: u32 = 384;
Expand All @@ -10,7 +11,7 @@ const BUF_SIZE: u32 = WIDTH * HEIGHT * 3;
const SIZE: f64 = 0.000000001;
const MAX_ITER: u32 = 1000;

pub fn draw_fractal_image(scale: f64) -> Option<Vec<u8>> {
pub fn draw_fractal_image(scale: f64) -> Result<Vec<u8>, ExampleError> {
let point_x: f64 = -0.5557506;
let point_y: f64 = -0.55560;
let mut buffer: Vec<u8> = vec![0; BUF_SIZE as usize];
Expand All @@ -27,8 +28,8 @@ pub fn draw_fractal_image(scale: f64) -> Option<Vec<u8>> {
);

match result {
Ok(_) => Some(image_data),
Err(_) => None,
Ok(_) => Ok(image_data),
Err(error) => Err(ExampleError(error.into())),
}
}

Expand Down
21 changes: 14 additions & 7 deletions flutter_ffi_plugin/example/native/sample_crate/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,24 +1,26 @@
//! This crate is written for Rinf demonstrations.

mod common;
mod error;
mod fractal;

use common::*;
use error::ExampleError;

pub use fractal::draw_fractal_image;

// `machineid_rs` only supports desktop platforms.
#[cfg(any(target_os = "windows", target_os = "macos", target_os = "linux"))]
pub fn get_hardward_id() -> Result<String> {
pub fn get_hardward_id() -> Result<String, ExampleError> {
let mut builder = machineid_rs::IdBuilder::new(machineid_rs::Encryption::MD5);
builder
.add_component(machineid_rs::HWIDComponent::SystemID)
.add_component(machineid_rs::HWIDComponent::CPUCores);
let hwid = builder.build("mykey")?;
let hwid = builder
.build("mykey")
.map_err(|error| ExampleError(error.into()))?;
Ok(hwid)
}
#[cfg(not(any(target_os = "windows", target_os = "macos", target_os = "linux")))]
pub fn get_hardward_id() -> Result<String> {
pub fn get_hardward_id() -> Result<String, ExampleError> {
Ok(String::from("UNSUPPORTED"))
}

Expand All @@ -29,7 +31,12 @@ pub fn get_current_time() -> DateTime<offset::Local> {
}

// `reqwest` supports all platforms, including web.
pub async fn fetch_from_web_api(url: &str) -> Result<String> {
let fetched = reqwest::get(url).await?.text().await?;
pub async fn fetch_from_web_api(url: &str) -> Result<String, ExampleError> {
let fetched = reqwest::get(url)
.await
.map_err(|error| ExampleError(error.into()))?
.text()
.await
.map_err(|error| ExampleError(error.into()))?;
Ok(fetched)
}
3 changes: 0 additions & 3 deletions rust_crate/src/common.rs

This file was deleted.

47 changes: 47 additions & 0 deletions rust_crate/src/error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
use std::error::Error;
use std::fmt;

#[derive(Debug)]
pub enum RinfError {
LockDartIsolate,
NoDartIsolate,
BuildRuntime,
LockMessageChannel,
NoMessageChannel,
MessageReceiverTaken,
DecodeMessage,
NoSignalHandler,
}

impl fmt::Display for RinfError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
RinfError::LockDartIsolate => {
write!(f, "Could not acquire the Dart isolate lock.")
}
RinfError::NoDartIsolate => {
write!(f, "Dart isolate for Rust signals was not created.")
}
RinfError::BuildRuntime => {
write!(f, "Could not build the tokio runtime.")
}
RinfError::LockMessageChannel => {
write!(f, "Could not acquire the message channel lock.")
}
RinfError::NoMessageChannel => {
write!(f, "Message channel was not created.",)
}
RinfError::MessageReceiverTaken => {
write!(f, "Each Dart signal receiver can be taken only once.")
}
RinfError::DecodeMessage => {
write!(f, "Could not decode the message.")
}
RinfError::NoSignalHandler => {
write!(f, "Could not find the handler for Dart signal.")
}
}
}
}

impl Error for RinfError {}
Loading