use std::{
cell::{Cell, RefCell},
collections::VecDeque,
future::Future,
pin::Pin,
rc::{Rc, Weak},
task::{Context, Poll, Waker},
};
use super::{tree, Backend, BackendStage};
use crate::component::Component;
use crate::component::PrerenderableComponent;
use crate::error::Error;
use crate::mount_point::{DynMountPoint, MountPoint};
use crate::template::ComponentTemplate;
#[must_use]
pub struct AsyncCallback<R: 'static> {
done: Rc<Cell<Option<R>>>,
waker: Rc<Cell<Option<Waker>>>,
}
impl<R: 'static> Future for AsyncCallback<R> {
type Output = R;
fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
if let Some(ret) = self.done.take() {
Poll::Ready(ret)
} else {
self.waker.set(Some(cx.waker().clone()));
Poll::Pending
}
}
}
impl<R: 'static> AsyncCallback<R> {
pub fn new() -> (Self, impl 'static + FnOnce(R)) {
let done = Rc::new(Cell::new(None));
let done2 = done.clone();
let waker = Rc::new(Cell::new(None));
let waker2 = waker.clone();
let callback = move |ret| {
done2.set(Some(ret));
match waker2.take() {
Some(waker) => {
let waker: Waker = waker;
waker.wake();
}
None => {}
}
};
(Self { done, waker }, callback)
}
}
pub(crate) enum BackendContextEvent<B: Backend> {
General(Box<dyn FnOnce(&mut EnteredBackendContext<B>)>),
}
pub struct BackendContext<B: Backend> {
inner: Rc<BackendContextInner<B>>,
}
struct BackendContextInner<B: Backend> {
initial_backend_stage: Cell<BackendStage>,
entered: RefCell<EnteredBackendContext<B>>,
event_queue: RefCell<VecDeque<BackendContextEvent<B>>>,
}
impl<B: Backend> Clone for BackendContext<B> {
fn clone(&self) -> Self {
Self {
inner: self.inner.clone(),
}
}
}
impl<B: Backend> BackendContext<B> {
pub fn new(backend: B) -> Self {
let initial_backend_stage = Cell::new(backend.backend_stage());
let entered = RefCell::new(EnteredBackendContext { backend, ctx: None });
let inner = Rc::new(BackendContextInner {
initial_backend_stage,
entered,
event_queue: Default::default(),
});
let w = Rc::downgrade(&inner);
inner.entered.borrow_mut().ctx = Some(w);
Self { inner }
}
pub fn initial_backend_stage(&self) -> BackendStage {
self.inner.initial_backend_stage.get()
}
fn generate_async_task(&self) {
let inner = self.inner.clone();
B::async_task(async move {
let entered = &mut inner.entered.borrow_mut();
loop {
let ev = inner.event_queue.borrow_mut().pop_front();
if let Some(ev) = ev {
match ev {
BackendContextEvent::General(f) => {
f(entered);
}
}
} else {
break;
}
}
});
}
#[inline]
pub fn enter<T: 'static, F>(&self, f: F) -> AsyncCallback<T>
where
F: 'static + FnOnce(&mut EnteredBackendContext<B>) -> T,
{
let (fut, cb) = AsyncCallback::new();
let need_task = {
let event_queue = &mut self.inner.event_queue.borrow_mut();
event_queue.push_back(BackendContextEvent::General(Box::new(move |x| {
cb(f(x));
})));
event_queue.len() == 1
};
if need_task && self.inner.entered.try_borrow_mut().is_ok() {
self.generate_async_task();
}
fut
}
#[inline]
pub fn enter_sync<T, F>(&self, f: F) -> Result<T, F>
where
F: FnOnce(&mut EnteredBackendContext<B>) -> T,
{
if let Ok(mut entered) = self.inner.entered.try_borrow_mut() {
let need_task = self.inner.event_queue.borrow().is_empty();
let ret = f(&mut entered);
if need_task && !self.inner.event_queue.borrow().is_empty() {
self.generate_async_task();
}
Ok(ret)
} else {
Err(f)
}
}
pub async fn prerendering_data<C: PrerenderableComponent>(
query_data: &C::QueryData,
) -> PrerenderingData<C> {
PrerenderingData::new(C::prerendering_data(query_data).await)
}
}
pub struct EnteredBackendContext<B: Backend> {
backend: B,
ctx: Option<Weak<BackendContextInner<B>>>,
}
impl<B: Backend> EnteredBackendContext<B> {
pub fn attach<C: Component + ComponentTemplate<B>>(
&mut self,
init: impl FnOnce(&mut C),
) -> Result<MountPoint<B, C>, Error> {
let mut root = self.backend.root_mut();
MountPoint::attach(
&BackendContext {
inner: self.ctx.as_ref().unwrap().upgrade().unwrap(),
},
&mut root,
init,
)
}
pub fn prerendering_attach<C: PrerenderableComponent + ComponentTemplate<B> + 'static>(
&mut self,
prerendering_data: PrerenderingData<C>,
) -> Result<MountPoint<B, C>, Error> {
let mut root = self.backend.root_mut();
MountPoint::attach(
&BackendContext {
inner: self.ctx.as_ref().unwrap().upgrade().unwrap(),
},
&mut root,
|comp| {
PrerenderableComponent::apply_prerendering_data(comp, prerendering_data.data);
},
)
}
pub fn detach<C: Component + ComponentTemplate<B>>(
&mut self,
mount_point: &mut MountPoint<B, C>,
) {
let mut root = self.backend.root_mut();
mount_point.detach(&mut root);
}
pub fn detach_dyn(&mut self, mount_point: &mut DynMountPoint<B>) {
let mut root = self.backend.root_mut();
mount_point.detach(&mut root);
}
#[inline]
pub fn root_component_with<C: Component + ComponentTemplate<B>, R>(
&mut self,
mount_point: &MountPoint<B, C>,
f: impl FnOnce(&mut C) -> R,
) -> R {
let n = mount_point.root_component();
f(&mut n.component().borrow_mut())
}
#[inline]
pub fn root(&self) -> tree::ForestNode<B::GeneralElement> {
self.backend.root()
}
#[inline]
pub fn root_mut(&mut self) -> tree::ForestNodeMut<B::GeneralElement> {
self.backend.root_mut()
}
}
impl<B: Backend> std::ops::Deref for EnteredBackendContext<B> {
type Target = B;
#[inline]
fn deref(&self) -> &Self::Target {
&self.backend
}
}
impl<B: Backend> std::ops::DerefMut for EnteredBackendContext<B> {
#[inline]
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.backend
}
}
pub struct PrerenderingData<C: PrerenderableComponent> {
data: C::PrerenderingData,
}
impl<C: PrerenderableComponent> PrerenderingData<C> {
#[inline]
pub fn new(data: C::PrerenderingData) -> Self {
Self { data }
}
#[inline]
pub fn get(&self) -> &C::PrerenderingData {
&self.data
}
#[inline]
pub fn unwrap(self) -> C::PrerenderingData {
self.data
}
}