Skip to content

Commit

Permalink
Merge pull request #381 from cunarist/rinf-error-types
Browse files Browse the repository at this point in the history
Return `RinfError` on errors
  • Loading branch information
temeddix authored Jun 18, 2024
2 parents c16674e + 531b4ce commit f8f688a
Show file tree
Hide file tree
Showing 15 changed files with 177 additions and 96 deletions.
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

0 comments on commit f8f688a

Please sign in to comment.