use async_trait::async_trait;
use std::{
cell::{Cell, Ref, RefCell},
marker::PhantomData,
rc::{Rc, Weak},
};
use crate::{
backend::{
context::AsyncCallback, tree::*, AsElementTag, Backend, BackendComponent,
BackendGeneralElement,
},
error::Error,
node::{DynNodeList, OwnerWeak, SlotChange, SlotKindTrait},
template::*,
BackendContext,
};
pub struct ComponentRc<C: 'static> {
inner: Rc<dyn UpdateScheduler<EnterType = C>>,
_phantom: PhantomData<C>,
}
impl<C: 'static> Clone for ComponentRc<C> {
fn clone(&self) -> Self {
Self {
inner: self.inner.clone(),
_phantom: PhantomData,
}
}
}
impl<C: 'static> ComponentRc<C> {
pub(crate) fn new(inner: Rc<dyn UpdateScheduler<EnterType = C>>) -> Self {
Self {
inner,
_phantom: PhantomData,
}
}
fn downgrade(&self) -> ComponentWeak<C> {
ComponentWeak {
inner: Rc::downgrade(&self.inner),
_phantom: PhantomData,
}
}
pub fn task<R: 'static>(&self, f: impl 'static + FnOnce(&mut C) -> R) {
self.inner.clone().enter_mut_detached(Box::new(move |c| {
f(c);
true
}));
}
pub fn task_with<R: 'static>(
&self,
f: impl 'static + FnOnce(&mut C, &mut ComponentMutCtx) -> R,
) {
self.inner.clone().enter_mut_detached(Box::new(move |c| {
let mut ctx = ComponentMutCtx { need_update: false };
f(c, &mut ctx);
ctx.need_update
}));
}
pub async fn update<R: 'static>(
&self,
f: impl 'static + FnOnce(&mut C) -> R,
) -> Result<R, Error> {
let ret = Rc::new(Cell::<Option<R>>::new(None));
let ret2 = ret.clone();
self.inner
.clone()
.enter_mut(Box::new(move |c| {
let r = f(c);
ret2.set(Some(r));
true
}))
.await?;
Ok(Rc::try_unwrap(ret)
.map_err(|_| "Enter callback failed")
.unwrap()
.into_inner()
.unwrap())
}
pub async fn update_with<R: 'static>(
&self,
f: impl 'static + FnOnce(&mut C, &mut ComponentMutCtx) -> R,
) -> Result<R, Error> {
let ret = Rc::new(Cell::<Option<R>>::new(None));
let ret2 = ret.clone();
self.inner
.clone()
.enter_mut(Box::new(move |c| {
let mut ctx = ComponentMutCtx { need_update: false };
let r = f(c, &mut ctx);
ret2.set(Some(r));
ctx.need_update
}))
.await?;
Ok(Rc::try_unwrap(ret)
.map_err(|_| "Enter callback failed")
.unwrap()
.into_inner()
.unwrap())
}
pub async fn get<R: 'static>(&self, f: impl 'static + FnOnce(&C) -> R) -> R {
let ret = Rc::new(Cell::<Option<R>>::new(None));
let ret2 = ret.clone();
self.inner
.clone()
.enter(Box::new(move |c| {
let r = f(c);
ret2.set(Some(r));
}))
.await;
Rc::try_unwrap(ret)
.map_err(|_| "Enter callback failed")
.unwrap()
.into_inner()
.unwrap()
}
}
pub struct ComponentWeak<C> {
inner: Weak<dyn UpdateScheduler<EnterType = C>>,
_phantom: PhantomData<C>,
}
impl<C> Clone for ComponentWeak<C> {
fn clone(&self) -> Self {
Self {
inner: self.inner.clone(),
_phantom: PhantomData,
}
}
}
impl<C: 'static> ComponentWeak<C> {
pub(crate) fn to_owner_weak(&self) -> Box<dyn OwnerWeak> {
self.clone_owner_weak()
}
pub fn upgrade(&self) -> Option<ComponentRc<C>> {
let inner = self.inner.upgrade()?;
Some(ComponentRc {
inner,
_phantom: PhantomData,
})
}
}
impl<C: 'static> OwnerWeak for ComponentWeak<C> {
fn apply_updates(&self) -> Result<(), Error> {
if let Some(x) = self.inner.upgrade() {
x.sync_update()?;
}
Ok(())
}
fn clone_owner_weak(&self) -> Box<dyn OwnerWeak> {
Box::new(Self {
inner: self.inner.clone(),
_phantom: PhantomData,
})
}
}
pub struct ComponentMutCtx {
need_update: bool,
}
impl ComponentMutCtx {
pub fn need_update(&mut self) {
self.need_update = true;
}
}
pub trait Component: 'static {
fn new() -> Self;
fn created(&self) {}
fn before_template_apply(&mut self) {}
}
#[async_trait(?Send)]
pub trait ComponentExt<B: Backend, C> {
type TemplateStructure;
fn template_structure(&self) -> Option<Ref<Self::TemplateStructure>>;
async fn apply_updates(&mut self) -> Result<(), Error>
where
C: 'static,
Self: 'static;
async fn update<R>(
&self,
f: impl 'static + for<'r> FnOnce(&'r mut Self) -> R,
) -> Result<R, Error>
where
R: 'static,
C: 'static,
Self: 'static;
fn rc(&self) -> ComponentRc<C>
where
C: 'static,
Self: 'static;
fn weak(&self) -> ComponentWeak<C>
where
C: 'static,
Self: 'static;
}
#[async_trait(?Send)]
impl<B: Backend, T: ComponentTemplate<B>> ComponentExt<B, Self> for T {
type TemplateStructure = T::TemplateStructure;
#[inline]
fn template_structure(&self) -> Option<Ref<Self::TemplateStructure>> {
<Self as ComponentTemplate<B>>::template(self).structure()
}
#[inline]
async fn apply_updates(&mut self) -> Result<(), Error>
where
T: 'static,
{
<Self as ComponentTemplate<B>>::template(self).mark_dirty();
self.rc().update(|_| {}).await
}
#[inline]
async fn update<R: 'static>(
&self,
f: impl 'static + for<'r> FnOnce(&'r mut Self) -> R,
) -> Result<R, Error>
where
T: 'static,
{
<Self as ComponentExt<B, Self>>::rc(self).update(f).await
}
#[inline]
fn rc(&self) -> ComponentRc<Self>
where
T: 'static,
{
self.template()
.component_rc()
.expect("Cannot get `ComponentRc` before initialization")
}
#[inline]
fn weak(&self) -> ComponentWeak<Self>
where
T: 'static,
{
self.template()
.component_weak()
.expect("Cannot get `ComponentWeak` before initialization")
}
}
#[async_trait]
pub trait PrerenderableComponent: Component {
type QueryData;
type PrerenderingData;
async fn prerendering_data(query_data: &Self::QueryData) -> Self::PrerenderingData;
fn apply_prerendering_data(&mut self, data: Self::PrerenderingData);
}
pub(crate) trait UpdateScheduler: 'static {
type EnterType;
fn enter(self: Rc<Self>, f: Box<dyn FnOnce(&Self::EnterType)>) -> AsyncCallback<()>;
fn enter_mut(
self: Rc<Self>,
f: Box<dyn FnOnce(&mut Self::EnterType) -> bool>,
) -> AsyncCallback<Result<(), Error>>;
fn enter_mut_detached(self: Rc<Self>, f: Box<dyn FnOnce(&mut Self::EnterType) -> bool>);
fn sync_update(&self) -> Result<(), Error>;
}
#[derive(Clone)]
pub struct ComponentNode<C: Component> {
inner: Rc<RefCell<C>>,
backend_element_token: ForestToken,
rc: ComponentRc<C>,
}
impl<C: Component> ComponentNode<C> {
fn new<B: Backend>(
c: C,
backend_context: BackendContext<B>,
forest_node_rc: ForestNodeRc<<B as Backend>::GeneralElement>,
owner_weak: Box<dyn OwnerWeak>,
) -> Self
where
C: ComponentTemplate<B>,
{
let inner = Rc::new(RefCell::new(c));
let backend_element_token = forest_node_rc.token();
let rc: Rc<dyn UpdateScheduler<EnterType = C>> = Rc::new(ComponentNodeInBackend {
inner: inner.clone(),
backend_context,
forest_node_rc,
owner_weak,
});
let rc = ComponentRc::new(rc);
Self {
inner,
backend_element_token,
rc,
}
}
pub(crate) fn component(&self) -> &RefCell<C> {
&self.inner
}
#[inline]
pub fn rc(&self) -> ComponentRc<C> {
self.rc.clone()
}
#[inline]
pub fn weak(&self) -> ComponentWeak<C> {
self.rc.downgrade()
}
}
struct ComponentNodeInBackend<B: Backend, C: ComponentTemplate<B> + Component> {
inner: Rc<RefCell<C>>,
backend_context: BackendContext<B>,
forest_node_rc: ForestNodeRc<B::GeneralElement>,
owner_weak: Box<dyn OwnerWeak>,
}
impl<B: Backend, C: ComponentTemplate<B> + Component> ComponentNodeInBackend<B, C> {
fn prepare_inner_changes(
this: &Rc<Self>,
f: Box<dyn FnOnce(&mut C) -> bool>,
) -> Result<(), Error> {
let has_slot_changes = {
let mut comp = this.inner.borrow_mut();
let force_schedule_update = f(&mut comp);
if <C as ComponentTemplate<B>>::template(&mut comp).clear_dirty()
|| force_schedule_update
{
let mut backend_element = this.forest_node_rc.borrow_mut();
<C as Component>::before_template_apply(&mut comp);
let has_slot_changes =
<C as ComponentTemplate<B>>::template_update_store_slot_changes(
&mut comp,
&this.backend_context,
&mut backend_element,
)?;
has_slot_changes
} else {
false
}
};
if has_slot_changes {
this.owner_weak.apply_updates()?;
}
Ok(())
}
}
impl<B: Backend, C: ComponentTemplate<B> + Component> UpdateScheduler
for ComponentNodeInBackend<B, C>
{
type EnterType = C;
#[inline]
fn enter(self: Rc<Self>, f: Box<dyn FnOnce(&Self::EnterType)>) -> AsyncCallback<()> {
self.backend_context
.clone()
.enter(move |_| f(&self.inner.borrow()))
}
#[inline]
fn enter_mut(
self: Rc<Self>,
f: Box<dyn FnOnce(&mut Self::EnterType) -> bool>,
) -> AsyncCallback<Result<(), Error>> {
self.backend_context
.clone()
.enter::<Result<(), Error>, _>(move |_| Self::prepare_inner_changes(&self, f))
}
#[inline]
fn enter_mut_detached(self: Rc<Self>, f: Box<dyn FnOnce(&mut Self::EnterType) -> bool>) {
let _ = self.backend_context.clone().enter::<(), _>(move |_| {
if let Err(err) = Self::prepare_inner_changes(&self, f) {
panic!("{}", err);
}
});
}
#[inline]
fn sync_update(&self) -> Result<(), Error> {
let has_slot_changes = {
let mut comp = self.inner.borrow_mut();
<C as ComponentTemplate<B>>::template(&mut comp).clear_dirty();
let mut backend_element = self.forest_node_rc.borrow_mut();
<C as Component>::before_template_apply(&mut comp);
let has_slot_changes = <C as ComponentTemplate<B>>::template_update_store_slot_changes(
&mut comp,
&self.backend_context,
&mut backend_element,
)?;
has_slot_changes
};
if has_slot_changes {
self.owner_weak.apply_updates()?;
}
Ok(())
}
}
impl<C: Component + ComponentSlotKind> AsElementTag for C {
type Target = ComponentNode<C>;
type SlotChildren = <C as ComponentSlotKind>::SlotChildren<DynNodeList>;
}
impl<B: Backend, C: ComponentTemplate<B> + Component> BackendComponent<B> for ComponentNode<C> {
type SlotData = <C as ComponentSlotKind>::SlotData;
type UpdateTarget = C;
type UpdateContext = bool;
#[inline]
fn init<'b>(
backend_context: &'b BackendContext<B>,
owner: &'b mut ForestNodeMut<B::GeneralElement>,
owner_weak: &Box<dyn OwnerWeak>,
) -> Result<(Self, ForestNodeRc<B::GeneralElement>), Error>
where
Self: Sized,
{
let backend_element = B::GeneralElement::create_virtual_element(owner)?;
let this = ComponentNode::new(
<C as Component>::new(),
backend_context.clone(),
backend_element.clone(),
owner_weak.clone_owner_weak(),
);
let init = TemplateInit {
updater: this.rc.downgrade(),
};
{
let mut comp = this.component().borrow_mut();
<C as ComponentTemplate<B>>::template_init(&mut comp, init);
}
Ok((this, backend_element))
}
#[inline]
fn create<'b>(
&'b mut self,
backend_context: &'b BackendContext<B>,
owner: &'b mut ForestNodeMut<<B as Backend>::GeneralElement>,
update_fn: Box<dyn 'b + FnOnce(&mut C, &mut bool)>,
slot_fn: &mut dyn FnMut(
&mut ForestNodeMut<B::GeneralElement>,
&ForestToken,
&Self::SlotData,
) -> Result<(), Error>,
) -> Result<(), Error> {
if let Ok(mut comp) = self.component().try_borrow_mut() {
let mut backend_element = owner.borrow_mut_token(&self.backend_element_token).unwrap();
let mut force_dirty = false;
update_fn(&mut comp, &mut force_dirty);
<C as Component>::before_template_apply(&mut comp);
<C as ComponentTemplate<B>>::template_create_or_update(
&mut comp,
backend_context,
&mut backend_element,
&mut |slot_change| match slot_change {
SlotChange::Added(n, t, d) => slot_fn(n, t, d),
_ => Err(Error::TreeNotCreated),
},
)?;
#[cfg(not(feature = "prerendering"))]
<C as Component>::created(&comp);
#[cfg(feature = "prerendering")]
if backend_context.initial_backend_stage() != crate::backend::BackendStage::Prerendering
{
<C as Component>::created(&comp);
}
Ok(())
} else {
Err(Error::RecursiveUpdate)
}
}
#[inline]
fn apply_updates<'b>(
&'b mut self,
backend_context: &'b BackendContext<B>,
owner: &'b mut ForestNodeMut<B::GeneralElement>,
update_fn: Box<dyn 'b + FnOnce(&mut C, &mut bool)>,
mut slot_fn: &mut dyn FnMut(
SlotChange<&mut ForestNodeMut<B::GeneralElement>, &ForestToken, &Self::SlotData>,
) -> Result<(), Error>,
) -> Result<(), Error> {
if let Ok(mut comp) = self.component().try_borrow_mut() {
let mut force_dirty = false;
update_fn(&mut comp, &mut force_dirty);
if <C as ComponentTemplate<B>>::template(&mut comp).clear_dirty() || force_dirty {
let mut backend_element =
owner.borrow_mut_token(&self.backend_element_token).unwrap();
<C as Component>::before_template_apply(&mut comp);
<C as ComponentTemplate<B>>::template_create_or_update(
&mut comp,
backend_context,
&mut backend_element,
&mut slot_fn,
)
} else {
let changes = <C as ComponentTemplate<B>>::template(&mut comp)
.extract_pending_slot_changes(Vec::with_capacity(0));
if changes.len() > 0 {
for slot_change in changes {
match slot_change {
SlotChange::Unchanged(..) => {}
SlotChange::DataChanged(_, t, _) => {
let addr = t.stable_addr();
slot_fn(SlotChange::Unchanged(
owner
.borrow_mut_token(&t)
.as_mut()
.ok_or(Error::ListChangeWrong)?,
&t,
&<C as ComponentTemplate<B>>::template(&mut comp)
.slot_scopes()
.get(addr)?
.1,
))?;
}
SlotChange::Added(_, t, _) => {
let addr = t.stable_addr();
slot_fn(SlotChange::Added(
owner
.borrow_mut_token(&t)
.as_mut()
.ok_or(Error::ListChangeWrong)?,
&t,
&<C as ComponentTemplate<B>>::template(&mut comp)
.slot_scopes()
.get(addr)?
.1,
))?;
}
SlotChange::Removed(t) => {
slot_fn(SlotChange::Removed(&t))?;
}
}
}
Ok(())
} else {
let mut backend_element =
owner.borrow_mut_token(&self.backend_element_token).unwrap();
<C as ComponentTemplate<B>>::for_each_slot_scope(
&mut comp,
&mut backend_element,
&mut slot_fn,
)
}
}
} else {
Err(Error::RecursiveUpdate)
}
}
}