use std::any::Any;
use std::panic;
use std::panic::{RefUnwindSafe, UnwindSafe};
use crate::ffi::{IntoDart, MessagePort};
use crate::rust2dart::{BoxIntoDart, IntoIntoDart, Rust2Dart, Rust2DartAction, TaskCallback};
use crate::support::WireSyncReturn;
use crate::{spawn, DartAbi, SyncReturn};
#[derive(Copy, Clone)]
pub enum FfiCallMode {
Normal,
Sync,
Stream,
}
#[derive(Clone)]
pub struct WrapInfo {
pub port: Option<MessagePort>,
pub debug_name: &'static str,
pub mode: FfiCallMode,
}
pub trait Handler {
fn wrap<PrepareFn, TaskFn, TaskRet, D, Er>(&self, wrap_info: WrapInfo, prepare: PrepareFn)
where
PrepareFn: FnOnce() -> TaskFn + UnwindSafe,
TaskFn: FnOnce(TaskCallback) -> Result<TaskRet, Er> + Send + UnwindSafe + 'static,
TaskRet: IntoIntoDart<D>,
D: IntoDart,
Er: IntoDart + 'static;
fn wrap_sync<SyncTaskFn, TaskRet, D, Er>(
&self,
wrap_info: WrapInfo,
sync_task: SyncTaskFn,
) -> WireSyncReturn
where
SyncTaskFn: FnOnce() -> Result<SyncReturn<TaskRet>, Er> + UnwindSafe,
TaskRet: IntoIntoDart<D>,
D: IntoDart,
Er: IntoDart + 'static;
}
pub struct SimpleHandler<E: Executor, EH: ErrorHandler> {
executor: E,
error_handler: EH,
}
impl<E: Executor, H: ErrorHandler> SimpleHandler<E, H> {
pub fn new(executor: E, error_handler: H) -> Self {
SimpleHandler {
executor,
error_handler,
}
}
}
pub type DefaultHandler =
SimpleHandler<ThreadPoolExecutor<ReportDartErrorHandler>, ReportDartErrorHandler>;
impl Default for DefaultHandler {
fn default() -> Self {
Self::new(
ThreadPoolExecutor::new(ReportDartErrorHandler),
ReportDartErrorHandler,
)
}
}
impl<E: Executor, EH: ErrorHandler> Handler for SimpleHandler<E, EH> {
fn wrap<PrepareFn, TaskFn, TaskRet, D, Er>(&self, wrap_info: WrapInfo, prepare: PrepareFn)
where
PrepareFn: FnOnce() -> TaskFn + UnwindSafe,
TaskFn: FnOnce(TaskCallback) -> Result<TaskRet, Er> + Send + UnwindSafe + 'static,
TaskRet: IntoIntoDart<D>,
D: IntoDart,
Er: IntoDart + 'static,
{
let _ = panic::catch_unwind(move || {
let wrap_info2 = wrap_info.clone();
if let Err(error) = panic::catch_unwind(move || {
let task = prepare();
self.executor.execute(wrap_info2, task);
}) {
self.error_handler
.handle_error(wrap_info.port.unwrap(), Error::Panic(error));
}
});
}
fn wrap_sync<SyncTaskFn, TaskRet, D, Er>(
&self,
wrap_info: WrapInfo,
sync_task: SyncTaskFn,
) -> WireSyncReturn
where
TaskRet: IntoIntoDart<D>,
D: IntoDart,
SyncTaskFn: FnOnce() -> Result<SyncReturn<TaskRet>, Er> + UnwindSafe,
Er: IntoDart + 'static,
{
panic::catch_unwind(move || {
let catch_unwind_result = panic::catch_unwind(move || {
match self.executor.execute_sync(wrap_info, sync_task) {
Ok(data) => {
wire_sync_from_data(data.0.into_into_dart(), Rust2DartAction::Success)
}
Err(err) => self
.error_handler
.handle_error_sync(Error::CustomError(Box::new(err))),
}
});
catch_unwind_result
.unwrap_or_else(|error| self.error_handler.handle_error_sync(Error::Panic(error)))
})
.unwrap_or_else(|_| wire_sync_from_data(None::<()>, Rust2DartAction::Panic))
}
}
pub trait Executor: RefUnwindSafe {
fn execute<TaskFn, TaskRet, D, Er>(&self, wrap_info: WrapInfo, task: TaskFn)
where
TaskFn: FnOnce(TaskCallback) -> Result<TaskRet, Er> + Send + UnwindSafe + 'static,
TaskRet: IntoIntoDart<D>,
D: IntoDart,
Er: IntoDart + 'static;
fn execute_sync<SyncTaskFn, TaskRet, D, Er>(
&self,
wrap_info: WrapInfo,
sync_task: SyncTaskFn,
) -> Result<SyncReturn<TaskRet>, Er>
where
SyncTaskFn: FnOnce() -> Result<SyncReturn<TaskRet>, Er> + UnwindSafe,
TaskRet: IntoIntoDart<D>,
D: IntoDart,
Er: IntoDart + 'static;
}
pub struct ThreadPoolExecutor<EH: ErrorHandler> {
error_handler: EH,
}
impl<EH: ErrorHandler> ThreadPoolExecutor<EH> {
pub fn new(error_handler: EH) -> Self {
ThreadPoolExecutor { error_handler }
}
}
impl<EH: ErrorHandler> Executor for ThreadPoolExecutor<EH> {
fn execute<TaskFn, TaskRet, D, Er>(&self, wrap_info: WrapInfo, task: TaskFn)
where
TaskFn: FnOnce(TaskCallback) -> Result<TaskRet, Er> + Send + UnwindSafe + 'static,
TaskRet: IntoIntoDart<D>,
D: IntoDart,
Er: IntoDart + 'static,
{
let eh = self.error_handler;
let eh2 = self.error_handler;
let WrapInfo { port, mode, .. } = wrap_info;
spawn!(|port: Option<MessagePort>| {
let port2 = port.as_ref().cloned();
let thread_result = panic::catch_unwind(move || {
let port2 = port2.expect("(worker) thread");
#[allow(clippy::clone_on_copy)]
let rust2dart = Rust2Dart::new(port2.clone());
let ret = task(TaskCallback::new(rust2dart.clone()))
.map(|e| e.into_into_dart().into_dart());
match ret {
Ok(result) => {
match mode {
FfiCallMode::Normal => {
rust2dart.success(result);
}
FfiCallMode::Stream => {
}
FfiCallMode::Sync => {
panic!("FfiCallMode::Sync should not call execute, please call execute_sync instead")
}
}
}
Err(error) => {
eh2.handle_error(port2, Error::CustomError(Box::new(error)));
}
};
});
if let Err(error) = thread_result {
eh.handle_error(port.expect("(worker) eh"), Error::Panic(error));
}
});
}
fn execute_sync<SyncTaskFn, TaskRet, D, Er>(
&self,
_wrap_info: WrapInfo,
sync_task: SyncTaskFn,
) -> Result<SyncReturn<TaskRet>, Er>
where
SyncTaskFn: FnOnce() -> Result<SyncReturn<TaskRet>, Er> + UnwindSafe,
TaskRet: IntoIntoDart<D>,
D: IntoDart,
Er: IntoDart,
{
sync_task()
}
}
pub enum Error {
CustomError(Box<dyn BoxIntoDart>),
Panic(Box<dyn Any + Send>),
}
fn error_to_string(panic_err: &Box<dyn Any + Send>) -> String {
match panic_err.downcast_ref::<&'static str>() {
Some(s) => *s,
None => match panic_err.downcast_ref::<String>() {
Some(s) => &s[..],
None => "Box<dyn Any>",
},
}
.to_string()
}
impl Error {
pub fn message(&self) -> String {
match self {
Error::CustomError(_e) => "Box<dyn BoxIntoDart>".to_string(),
Error::Panic(panic_err) => error_to_string(panic_err),
}
}
}
impl IntoDart for Error {
fn into_dart(self) -> DartAbi {
match self {
Error::CustomError(e) => e.box_into_dart(),
Error::Panic(panic_err) => error_to_string(&panic_err).into_dart(),
}
}
}
pub trait ErrorHandler: UnwindSafe + RefUnwindSafe + Copy + Send + 'static {
fn handle_error(&self, port: MessagePort, error: Error);
fn handle_error_sync(&self, error: Error) -> WireSyncReturn;
}
#[derive(Clone, Copy)]
pub struct ReportDartErrorHandler;
impl ErrorHandler for ReportDartErrorHandler {
fn handle_error(&self, port: MessagePort, error: Error) {
match error {
e @ Error::CustomError(_) => Rust2Dart::new(port).error(e),
e @ Error::Panic(_) => Rust2Dart::new(port).panic(e),
};
}
fn handle_error_sync(&self, error: Error) -> WireSyncReturn {
let result_code = (&error).into();
wire_sync_from_data(error.into_dart(), result_code)
}
}
fn wire_sync_from_data<T: IntoDart>(data: T, result_code: Rust2DartAction) -> WireSyncReturn {
let sync_return = vec![result_code.into_dart(), data.into_dart()].into_dart();
#[cfg(not(wasm))]
return crate::support::new_leak_box_ptr(sync_return);
#[cfg(wasm)]
return sync_return;
}