use std::{marker::PhantomData, ptr};
use dart_sys::Dart_Handle;
use derive_more::with_trait::Debug;
use flutter_rust_bridge::DartOpaque;
use futures::channel::oneshot;
use medea_macro::dart_bridge;
use crate::{
api::{
DART_HANDLER_PORT, DartValue, DartValueArg, Error as DartError,
propagate_panic,
},
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::{Error, dart::utils::dart_future::FutureFromDart};
extern "C" {
pub fn complete_proxy(
fut: Dart_Handle,
resolver: ptr::NonNull<FutureFromDart>,
) -> Result<(), Error>;
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn FutureFromDart__resolve_ok(
future: ptr::NonNull<FutureFromDart>,
val: DartValue,
) {
propagate_panic(move || {
let future = unsafe { Box::from_raw(future.as_ptr()) };
future.resolve_ok(val);
});
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn FutureFromDart__resolve_err(
future: ptr::NonNull<FutureFromDart>,
val: Dart_Handle,
) {
propagate_panic(move || {
let future = unsafe { Box::from_raw(future.as_ptr()) };
future.resolve_err(unsafe { Error::from_handle(val) });
});
}
#[derive(Debug)]
pub struct FutureFromDart(
#[debug("{_0:p}")] Box<dyn FnOnce(Result<DartValue, Error>)>,
);
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: Debug,
T: 'static,
{
let dart_fut = unsafe { 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()),
));
}));
unsafe {
future_from_dart::complete_proxy(
dart_fut.get(),
ptr::NonNull::from(Box::leak(Box::new(this))),
)
}
.unwrap();
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>(Dart_Handle, PhantomData<*const O>);
impl<O> DartFuture<O> {
#[must_use]
pub fn into_dart_opaque(self) -> DartOpaque {
DartOpaque::new(self.0.cast(), DART_HANDLER_PORT.get().unwrap())
}
}
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 {
#![expect( // for testing only
clippy::missing_safety_doc,
missing_docs,
reason = "for testing only"
)]
use std::cell::RefCell;
use dart_sys::Dart_Handle;
use super::{DartFuture, IntoDartFuture as _};
use crate::{
api::err::FormatException,
platform::dart::utils::{
dart_future::FutureFromDart, handle::DartHandle,
},
};
#[unsafe(no_mangle)]
pub unsafe extern "C" fn test__future_from_dart__int(
future: Dart_Handle,
) -> DartFuture<Result<i64, FormatException>> {
let future = unsafe { DartHandle::new(future) };
async move {
let val = unsafe { FutureFromDart::execute::<i64>(future.get()) }
.await
.unwrap();
Ok(val)
}
.into_dart_future()
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn test__future_from_dart__string(
future: Dart_Handle,
) -> DartFuture<Result<String, FormatException>> {
let future = unsafe { DartHandle::new(future) };
async move {
let val =
unsafe { FutureFromDart::execute::<String>(future.get()) }
.await
.unwrap();
Ok(val)
}
.into_dart_future()
}
type TestFutureHandleFunction = extern "C" fn(Dart_Handle);
thread_local! {
static TEST_FUTURE_HANDLE_FUNCTION: RefCell<
Option<TestFutureHandleFunction>
> = RefCell::default();
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn register__test__future_from_dart_handle_fn(
f: TestFutureHandleFunction,
) {
TEST_FUTURE_HANDLE_FUNCTION.set(Some(f));
}
#[expect(clippy::expect_used, reason = "intended behavior")]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn test__future_from_dart__handle(
future: Dart_Handle,
) -> DartFuture<Result<(), FormatException>> {
let future = unsafe { DartHandle::new(future) };
async move {
let val =
unsafe { FutureFromDart::execute::<DartHandle>(future.get()) }
.await
.unwrap();
TEST_FUTURE_HANDLE_FUNCTION.with_borrow(|f| {
f.expect("`TEST_FUTURE_HANDLE_FUNCTION` must be initialized")(
val.get(),
);
});
Ok(())
}
.into_dart_future()
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn test__future_from_dart__fails(
future: Dart_Handle,
) -> DartFuture<Result<i64, FormatException>> {
let future = unsafe { DartHandle::new(future) };
async move {
let val =
unsafe { FutureFromDart::execute::<i64>(future.get()) }.await;
Ok(val.is_err().into())
}
.into_dart_future()
}
}