use std::cell::{Cell, OnceCell};
use std::future::Future;
use std::pin::Pin;
use std::rc::Rc;
use std::task::{Context, Poll, Waker};
use crate::app::App;
use crate::change_notifier::Listener;
type Callback<T> = Box<dyn FnOnce(&mut App, T)>;
struct Shared<T> {
value: OnceCell<T>,
wakers: Cell<Vec<Waker>>,
callbacks: Cell<Vec<Callback<T>>>,
}
impl<T> Shared<T> {
fn new(value: Option<T>) -> Rc<Shared<T>> {
let shared = Shared {
value: OnceCell::new(),
wakers: Cell::new(Vec::new()),
callbacks: Cell::new(Vec::new()),
};
if let Some(value) = value {
let _ = shared.value.set(value);
}
Rc::new(shared)
}
}
pub struct Completer<T> {
shared: Rc<Shared<T>>,
}
impl<T> Completer<T> {
pub fn new() -> Completer<T> {
Completer {
shared: Shared::new(None),
}
}
pub fn completed(value: T) -> Completer<T> {
Completer {
shared: Shared::new(Some(value)),
}
}
pub fn is_completed(&self) -> bool {
self.shared.value.get().is_some()
}
pub fn future(&self) -> CompleterFuture<T> {
CompleterFuture {
shared: Rc::clone(&self.shared),
}
}
}
impl<T: Clone + 'static> Completer<T> {
pub fn complete(&self, app: &mut App, value: T) {
assert!(
self.shared.value.set(value.clone()).is_ok(),
"Completer.complete called twice (Dart's StateError)"
);
for waker in self.shared.wakers.take() {
waker.wake();
}
for callback in self.shared.callbacks.take() {
schedule(app, callback, value.clone());
}
}
}
impl<T> Default for Completer<T> {
fn default() -> Completer<T> {
Completer::new()
}
}
impl<T> Clone for Completer<T> {
fn clone(&self) -> Completer<T> {
Completer {
shared: Rc::clone(&self.shared),
}
}
}
fn schedule<T: 'static>(app: &mut App, callback: Callback<T>, value: T) {
let pending = Cell::new(Some((callback, value)));
app.schedule_microtask(Listener::new(move |app| {
if let Some((callback, value)) = pending.take() {
callback(app, value);
}
}));
}
pub struct CompleterFuture<T> {
shared: Rc<Shared<T>>,
}
impl<T> CompleterFuture<T> {
pub fn ready(value: T) -> CompleterFuture<T> {
Completer::completed(value).future()
}
pub fn is_completed(&self) -> bool {
self.shared.value.get().is_some()
}
}
impl<T: Clone + 'static> CompleterFuture<T> {
pub fn spawn(app: &App, future: impl Future<Output = T> + 'static) -> CompleterFuture<T> {
let completer = Completer::new();
let result = completer.future();
app.spawn(async move |cx| {
let value = future.await;
cx.update(|app| completer.complete(app, value));
});
result
}
pub fn peek(&self) -> Option<T> {
self.shared.value.get().cloned()
}
pub fn then(self, app: &mut App, callback: impl FnOnce(&mut App, T) + 'static) {
match self.shared.value.get() {
Some(value) => schedule(app, Box::new(callback), value.clone()),
None => {
let mut callbacks = self.shared.callbacks.take();
callbacks.push(Box::new(callback));
self.shared.callbacks.set(callbacks);
}
}
}
}
impl<T> Clone for CompleterFuture<T> {
fn clone(&self) -> CompleterFuture<T> {
CompleterFuture {
shared: Rc::clone(&self.shared),
}
}
}
impl<T: Clone> Future for CompleterFuture<T> {
type Output = T;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<T> {
if let Some(value) = self.shared.value.get() {
return Poll::Ready(value.clone());
}
let waker = cx.waker();
let mut wakers = self.shared.wakers.take();
if !wakers.iter().any(|known| known.will_wake(waker)) {
wakers.push(waker.clone());
}
self.shared.wakers.set(wakers);
Poll::Pending
}
}
pub async fn wait_all<T>(futures: impl IntoIterator<Item = impl Future<Output = T>>) -> Vec<T> {
let mut values = Vec::new();
for future in futures {
values.push(future.await);
}
values
}