use std::{future::Future, rc::Rc};
use godot::{classes::Engine, prelude::*};
use tokio::{
runtime::{self, Runtime},
task::JoinHandle,
};
#[derive(GodotClass)]
#[class(base=Object)]
pub struct AsyncRuntime {
base: Base<Object>,
runtime: Rc<Runtime>,
}
#[godot_api]
impl IObject for AsyncRuntime {
fn init(base: Base<Object>) -> Self {
#[cfg(feature = "single-thread")]
let runtime = runtime::Builder::new_current_thread()
.enable_all()
.build()
.unwrap();
#[cfg(feature = "multi-thread")]
let runtime = runtime::Builder::new_multi_thread()
.enable_all()
.build()
.unwrap();
Self {
base,
runtime: Rc::new(runtime),
}
}
}
#[godot_api]
impl AsyncRuntime {
pub const SINGLETON: &'static str = "Tokio";
fn singleton() -> Option<Gd<AsyncRuntime>> {
match Engine::singleton().get_singleton(Self::SINGLETON) {
Some(singleton) => Some(singleton.cast::<Self>()),
None => None,
}
}
pub fn runtime() -> Rc<Runtime> {
match Self::singleton() {
Some(singleton) => {
let bind = singleton.bind();
Rc::clone(&bind.runtime)
}
None => {
Engine::singleton()
.register_singleton(AsyncRuntime::SINGLETON, &AsyncRuntime::new_alloc());
let singleton = Self::singleton()
.expect("Engine was not able to register, or get `AsyncRuntime` singleton!");
let bind = singleton.bind();
Rc::clone(&bind.runtime)
}
}
}
pub fn spawn<F>(future: F) -> tokio::task::JoinHandle<F::Output>
where
F: Future + Send + 'static,
F::Output: Send + 'static,
{
Self::runtime().spawn(future)
}
pub fn block_on<F>(future: F) -> F::Output
where
F: Future,
{
Self::runtime().block_on(future)
}
pub fn spawn_blocking<F, R>(&self, func: F) -> JoinHandle<R>
where
F: FnOnce() -> R + Send + 'static,
R: Send + 'static,
{
Self::runtime().spawn_blocking(func)
}
}