use std::future::Future;
use std::marker::PhantomData;
use std::sync::Arc;
use futures_task::{LocalFutureObj, LocalSpawn, SpawnError};
use gdnative_core::core_types::{ToVariant, Variant};
use gdnative_core::export::{FromVarargs, Method, NativeClass, Varargs};
use gdnative_core::log::{self, Site};
use gdnative_core::object::TInstance;
use crate::rt::Context;
pub trait AsyncMethod<C: NativeClass>: Send + Sync + 'static {
fn spawn_with(&self, spawner: Spawner<'_, C>);
#[inline]
fn site() -> Option<Site<'static>> {
None
}
}
pub trait StaticArgsAsyncMethod<C: NativeClass>: Send + Sync + 'static {
type Args: FromVarargs;
fn spawn_with(&self, spawner: Spawner<'_, C, Self::Args>);
#[inline]
fn site() -> Option<Site<'static>> {
None
}
}
#[derive(Clone, Copy, Default, Debug)]
pub struct StaticArgs<F> {
f: F,
}
impl<F> StaticArgs<F> {
#[inline]
pub fn new(f: F) -> Self {
StaticArgs { f }
}
}
impl<C: NativeClass, F: StaticArgsAsyncMethod<C>> AsyncMethod<C> for StaticArgs<F> {
#[inline]
fn spawn_with(&self, spawner: Spawner<'_, C>) {
let spawner = spawner.try_map_args(|mut args| match args.read_many::<F::Args>() {
Ok(parsed) => {
if let Err(err) = args.done() {
err.with_site(F::site().unwrap_or_default()).log_error();
return None;
}
Some(parsed)
}
Err(errors) => {
for err in errors {
err.with_site(F::site().unwrap_or_default()).log_error();
}
None
}
});
match spawner {
Ok(spawner) => F::spawn_with(&self.f, spawner),
Err(spawner) => spawner.spawn(|_context, _this, ()| async { Variant::nil() }),
}
}
#[inline]
fn site() -> Option<Site<'static>> {
F::site()
}
}
pub struct Spawner<'a, C: NativeClass, A = Varargs<'a>> {
sp: &'static dyn LocalSpawn,
ctx: Context,
this: TInstance<'a, C>,
args: A,
result: &'a mut Option<Result<(), SpawnError>>,
_marker: PhantomData<*const ()>,
}
impl<'a, C: NativeClass, A> Spawner<'a, C, A> {
fn try_map_args<F, R>(self, f: F) -> Result<Spawner<'a, C, R>, Spawner<'a, C, ()>>
where
F: FnOnce(A) -> Option<R>,
{
let Spawner {
sp,
ctx,
this,
args,
result,
..
} = self;
match f(args) {
Some(args) => Ok(Spawner {
sp,
ctx,
this,
args,
result,
_marker: PhantomData,
}),
None => Err(Spawner {
sp,
ctx,
this,
args: (),
result,
_marker: PhantomData,
}),
}
}
pub fn spawn<F, R>(self, f: F)
where
F: FnOnce(Arc<Context>, TInstance<'_, C>, A) -> R,
R: Future<Output = Variant> + 'static,
{
let ctx = Arc::new(self.ctx);
let future = f(Arc::clone(&ctx), self.this, self.args);
*self.result = Some(
self.sp
.spawn_local_obj(LocalFutureObj::new(Box::new(async move {
let value = future.await;
ctx.resolve(value);
}))),
);
}
}
#[derive(Clone, Copy, Default, Debug)]
pub struct Async<F> {
f: F,
}
impl<F> Async<F> {
#[inline]
pub fn new(f: F) -> Self {
Async { f }
}
}
impl<C: NativeClass, F: AsyncMethod<C>> Method<C> for Async<F> {
fn call(&self, this: TInstance<'_, C>, args: Varargs<'_>) -> Variant {
if let Some(sp) = crate::executor::local_spawn() {
let ctx = Context::new();
let func_state = ctx.func_state();
let mut result = None;
self.f.spawn_with(Spawner {
sp,
ctx,
this,
args,
result: &mut result,
_marker: PhantomData,
});
match result {
Some(Ok(())) => func_state.to_variant(),
Some(Err(err)) => {
log::error(
Self::site().unwrap_or_default(),
format_args!("unable to spawn future: {err}"),
);
Variant::nil()
}
None => {
log::error(
Self::site().unwrap_or_default(),
format_args!("implementation did not spawn a future"),
);
Variant::nil()
}
}
} else {
log::error(
Self::site().unwrap_or_default(),
"a global executor must be set before any async methods can be called on this thread",
);
Variant::nil()
}
}
fn site() -> Option<Site<'static>> {
F::site()
}
}