use std::{fmt, future::Future, marker::PhantomData, ptr};
use dart_sys::Dart_Handle;
use flutter_rust_bridge::DartOpaque;
use futures::channel::oneshot;
use medea_macro::dart_bridge;
use crate::{
api::{
propagate_panic, utils::new_dart_opaque, DartValue, DartValueArg,
Error as DartError,
},
platform::{
dart::{error::Error, utils::Completer},
spawn,
utils::handle::DartHandle,
},
};
#[dart_bridge("flutter/lib/src/native/ffi/future.g.dart")]
mod future_from_dart {
use std::ptr;
use dart_sys::Dart_Handle;
use crate::platform::dart::utils::dart_future::FutureFromDart;
extern "C" {
pub fn complete_proxy(
fut: Dart_Handle,
resolver: ptr::NonNull<FutureFromDart>,
);
}
}
#[no_mangle]
pub unsafe extern "C" fn FutureFromDart__resolve_ok(
future: ptr::NonNull<FutureFromDart>,
val: DartValue,
) {
propagate_panic(move || {
let future = Box::from_raw(future.as_ptr());
future.resolve_ok(val);
});
}
#[no_mangle]
pub unsafe extern "C" fn FutureFromDart__resolve_err(
future: ptr::NonNull<FutureFromDart>,
val: Dart_Handle,
) {
propagate_panic(move || {
let future = Box::from_raw(future.as_ptr());
future.resolve_err(Error::from_handle(val));
});
}
pub struct FutureFromDart(Box<dyn FnOnce(Result<DartValue, Error>)>);
impl fmt::Debug for FutureFromDart {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_tuple("FutureFromDart")
.field(&format!("{:p}", self.0))
.finish()
}
}
impl FutureFromDart {
pub unsafe fn execute<T>(
dart_fut: Dart_Handle,
) -> impl Future<Output = Result<T, Error>>
where
DartValueArg<T>: TryInto<T>,
<DartValueArg<T> as TryInto<T>>::Error: fmt::Debug,
T: 'static,
{
let dart_fut = DartHandle::new(dart_fut);
let (tx, rx) = oneshot::channel();
let this = Self(Box::new(|res| {
drop(tx.send(
res.map(|val| DartValueArg::<T>::from(val).try_into().unwrap()),
));
}));
future_from_dart::complete_proxy(
dart_fut.get(),
ptr::NonNull::from(Box::leak(Box::new(this))),
);
async move { rx.await.unwrap() }
}
fn resolve_ok(self, val: DartValue) {
(self.0)(Ok(val));
}
fn resolve_err(self, err: Error) {
(self.0)(Err(err));
}
}
#[derive(Debug)]
#[repr(transparent)]
pub struct DartFuture<O>(
#[allow(unused_tuple_struct_fields)] Dart_Handle, PhantomData<*const O>,
);
impl<O> DartFuture<O> {
#[must_use]
pub fn into_dart_opaque(self) -> DartOpaque {
unsafe { new_dart_opaque(self.0) }
}
}
pub trait IntoDartFuture {
type Output;
fn into_dart_future(self) -> DartFuture<Self::Output>;
}
impl<Fut, Ok, Err> IntoDartFuture for Fut
where
Fut: Future<Output = Result<Ok, Err>> + 'static,
Ok: Into<DartValue> + 'static,
Err: Into<DartError>,
{
type Output = Fut::Output;
fn into_dart_future(self) -> DartFuture<Fut::Output> {
let completer = Completer::new();
let dart_future = completer.future();
spawn(async move {
match self.await {
Ok(ok) => {
completer.complete(ok);
}
Err(e) => {
completer.complete_error(e.into());
}
}
});
DartFuture(dart_future, PhantomData)
}
}
#[cfg(feature = "mockable")]
pub mod tests {
#![allow(clippy::missing_safety_doc)]
use dart_sys::Dart_Handle;
use crate::{
api::err::FormatException,
platform::dart::utils::{
dart_future::FutureFromDart, handle::DartHandle,
},
};
use super::{DartFuture, IntoDartFuture};
#[no_mangle]
pub unsafe extern "C" fn test__future_from_dart__int(
future: Dart_Handle,
) -> DartFuture<Result<i64, FormatException>> {
let future = DartHandle::new(future);
async move {
let val =
FutureFromDart::execute::<i64>(future.get()).await.unwrap();
Ok(val)
}
.into_dart_future()
}
#[no_mangle]
pub unsafe extern "C" fn test__future_from_dart__string(
future: Dart_Handle,
) -> DartFuture<Result<String, FormatException>> {
let future = DartHandle::new(future);
async move {
let val = FutureFromDart::execute::<String>(future.get())
.await
.unwrap();
Ok(val)
}
.into_dart_future()
}
type TestFutureHandleFunction = extern "C" fn(Dart_Handle);
static mut TEST_FUTURE_HANDLE_FUNCTION: Option<TestFutureHandleFunction> =
None;
#[no_mangle]
pub unsafe extern "C" fn register__test__future_from_dart_handle_fn(
f: TestFutureHandleFunction,
) {
TEST_FUTURE_HANDLE_FUNCTION = Some(f);
}
#[no_mangle]
pub unsafe extern "C" fn test__future_from_dart__handle(
future: Dart_Handle,
) -> DartFuture<Result<(), FormatException>> {
let future = DartHandle::new(future);
async move {
let val = FutureFromDart::execute::<DartHandle>(future.get())
.await
.unwrap();
(TEST_FUTURE_HANDLE_FUNCTION.unwrap())(val.get());
Ok(())
}
.into_dart_future()
}
#[no_mangle]
pub unsafe extern "C" fn test__future_from_dart__fails(
future: Dart_Handle,
) -> DartFuture<Result<i64, FormatException>> {
let future = DartHandle::new(future);
async move {
let val = FutureFromDart::execute::<i64>(future.get()).await;
Ok(val.is_err().into())
}
.into_dart_future()
}
}