skima 0.1.0

Rust structural UI library for web
Documentation
use std::any::{Any, TypeId};
use std::cell::RefCell;
use std::marker::PhantomData;
use std::rc::Rc;

use crate::tree::Tree;
use crate::web::{Backend, Markup};

pub struct ConstantContext<B: Backend> {
	tree: Tree<B>,
}

impl<B: Backend> ConstantContext<B> {
	pub fn with_memo<T: Clone + Into<Rc<dyn Any>> + 'static>(
		&mut self,
		func: impl FnOnce() -> T,
	) -> T
	where
		Rc<dyn Any>: TryInto<T>,
	{
		let tid = TypeId::of::<T>();
		let hash = fxhash::hash64(&tid);
		let mut memo = self.tree.data.borrow_mut();
		if let Some(item) = memo.get(&hash) {
			Rc::try_into(item.clone()).map_err(|_| ()).unwrap()
		} else {
			let t = func();
			memo.insert(hash, t.clone().into());
			t
		}
	}
}

pub struct ConstantComponentFactory<
	F: FnOnce(&mut ConstantContext<B>) -> M + 'static,
	M: Markup<B> + 'static,
	B: Backend + 'static,
> {
	factory: RefCell<Option<F>>,
	rendered: RefCell<Option<M>>,
	_b: PhantomData<B>,
}

pub fn constant<F, M, B>(factory: F) -> impl Markup<B>
where
	F: FnOnce(&mut ConstantContext<B>) -> M + 'static,
	M: Markup<B> + 'static,
	B: Backend + 'static,
{
	ConstantComponentFactory {
		factory: RefCell::new(Some(factory)),
		rendered: RefCell::new(None),
		_b: PhantomData,
	}
}

impl<F, M, B> Markup<B> for ConstantComponentFactory<F, M, B>
where
	F: FnOnce(&mut ConstantContext<B>) -> M + 'static,
	M: Markup<B> + 'static,
	B: Backend + 'static,
{
	fn has_own_node() -> bool {
		M::has_own_node()
	}

	fn render(&self, tree: &Tree<B>) {
		let mut context = ConstantContext { tree: tree.clone() };

		if let Some(factory) = self.factory.take() {
			let markup = factory(&mut context);
			markup.render(tree);
			self.rendered.replace(Some(markup));
		} else if let Some(markup) = self.rendered.borrow().as_ref() {
			markup.render(tree);
		}
	}

	fn diff(&self, prev: &Self, tree: &Tree<B>) {
		let mut context = ConstantContext { tree: tree.clone() };
		if let Some(factory) = self.factory.take() {
			let markup = factory(&mut context);
			markup.diff(prev.rendered.borrow().as_ref().unwrap(), tree);
			self.rendered.replace(Some(markup));
		} else if let Some(markup) = self.rendered.borrow().as_ref() {
			markup.diff(prev.rendered.borrow().as_ref().unwrap(), tree);
		}
	}

	fn drop(&self, tree: &Tree<B>, should_unmount: bool) {
		if let Some(markup) = self.rendered.borrow().as_ref() {
			markup.drop(tree, should_unmount);
		}

		tree.clear();
	}
}