290 lines
8.7 KiB
Rust
290 lines
8.7 KiB
Rust
use std::{
|
|
cell::{Cell, RefCell},
|
|
fmt::Arguments,
|
|
};
|
|
|
|
use bumpalo::boxed::Box as BumpBox;
|
|
use bumpalo::Bump;
|
|
use std::future::Future;
|
|
|
|
use crate::{
|
|
any_props::{AnyProps, VProps},
|
|
arena::ElementId,
|
|
innerlude::{DynamicNode, EventHandler, VComponent, VFragment, VText},
|
|
Attribute, AttributeValue, Element, LazyNodes, Properties, Scope, ScopeState, VNode,
|
|
};
|
|
|
|
impl ScopeState {
|
|
/// Create some text that's allocated along with the other vnodes
|
|
pub fn text<'a>(&'a self, args: Arguments) -> DynamicNode<'a> {
|
|
let (text, _) = self.raw_text(args);
|
|
DynamicNode::Text(VText {
|
|
id: Cell::new(ElementId(0)),
|
|
value: text,
|
|
})
|
|
}
|
|
|
|
pub fn raw_text_inline<'a>(&'a self, args: Arguments) -> &'a str {
|
|
self.raw_text(args).0
|
|
}
|
|
|
|
pub fn raw_text<'a>(&'a self, args: Arguments) -> (&'a str, bool) {
|
|
match args.as_str() {
|
|
Some(static_str) => (static_str, true),
|
|
None => {
|
|
use bumpalo::core_alloc::fmt::Write;
|
|
let mut str_buf = bumpalo::collections::String::new_in(self.bump());
|
|
str_buf.write_fmt(args).unwrap();
|
|
(str_buf.into_bump_str(), false)
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn fragment_from_iter<'a, 'c, I>(
|
|
&'a self,
|
|
node_iter: impl IntoDynNode<'a, I> + 'c,
|
|
) -> DynamicNode {
|
|
node_iter.into_vnode(self)
|
|
}
|
|
|
|
/// Create a new [`Attribute`]
|
|
pub fn attr<'a>(
|
|
&'a self,
|
|
name: &'static str,
|
|
value: impl IntoAttributeValue<'a>,
|
|
namespace: Option<&'static str>,
|
|
volatile: bool,
|
|
) -> Attribute<'a> {
|
|
Attribute {
|
|
name,
|
|
namespace,
|
|
volatile,
|
|
value: value.into_value(self.bump()),
|
|
mounted_element: Cell::new(ElementId(0)),
|
|
}
|
|
}
|
|
|
|
/// Create a new [`VNode::Component`]
|
|
pub fn component<'a, P, A, F: ComponentReturn<'a, A>>(
|
|
&'a self,
|
|
component: fn(Scope<'a, P>) -> F,
|
|
props: P,
|
|
fn_name: &'static str,
|
|
) -> DynamicNode<'a>
|
|
where
|
|
P: Properties + 'a,
|
|
{
|
|
let as_component = component;
|
|
let vcomp = VProps::new(as_component, P::memoize, props);
|
|
let as_dyn: Box<dyn AnyProps<'a>> = Box::new(vcomp);
|
|
let extended: Box<dyn AnyProps> = unsafe { std::mem::transmute(as_dyn) };
|
|
|
|
// let as_dyn: &dyn AnyProps = self.bump().alloc(vcomp);
|
|
// todo: clean up borrowed props
|
|
// if !P::IS_STATIC {
|
|
// let vcomp = &*vcomp;
|
|
// let vcomp = unsafe { std::mem::transmute(vcomp) };
|
|
// self.scope.items.borrow_mut().borrowed_props.push(vcomp);
|
|
// }
|
|
|
|
DynamicNode::Component(VComponent {
|
|
name: fn_name,
|
|
render_fn: component as *const (),
|
|
static_props: P::IS_STATIC,
|
|
props: Cell::new(Some(extended)),
|
|
scope: Cell::new(None),
|
|
})
|
|
}
|
|
|
|
/// Create a new [`EventHandler`] from an [`FnMut`]
|
|
pub fn event_handler<'a, T>(&'a self, f: impl FnMut(T) + 'a) -> EventHandler<'a, T> {
|
|
let handler: &mut dyn FnMut(T) = self.bump().alloc(f);
|
|
let caller = unsafe { BumpBox::from_raw(handler as *mut dyn FnMut(T)) };
|
|
let callback = RefCell::new(Some(caller));
|
|
EventHandler { callback }
|
|
}
|
|
}
|
|
|
|
pub trait ComponentReturn<'a, A = ()> {
|
|
fn as_return(self, cx: &'a ScopeState) -> RenderReturn<'a>;
|
|
}
|
|
impl<'a> ComponentReturn<'a> for Element<'a> {
|
|
fn as_return(self, _cx: &ScopeState) -> RenderReturn<'a> {
|
|
RenderReturn::Sync(self)
|
|
}
|
|
}
|
|
|
|
pub struct AsyncMarker;
|
|
impl<'a, F> ComponentReturn<'a, AsyncMarker> for F
|
|
where
|
|
F: Future<Output = Element<'a>> + 'a,
|
|
{
|
|
fn as_return(self, cx: &'a ScopeState) -> RenderReturn<'a> {
|
|
let f: &mut dyn Future<Output = Element<'a>> = cx.bump().alloc(self);
|
|
let boxed = unsafe { BumpBox::from_raw(f) };
|
|
let pined: BumpBox<_> = boxed.into();
|
|
RenderReturn::Async(pined)
|
|
}
|
|
}
|
|
|
|
pub enum RenderReturn<'a> {
|
|
/// A currently-available element
|
|
Sync(Element<'a>),
|
|
|
|
/// An ongoing future that will resolve to a [`Element`]
|
|
Async(BumpBox<'a, dyn Future<Output = Element<'a>> + 'a>),
|
|
}
|
|
|
|
impl<'a> RenderReturn<'a> {
|
|
pub(crate) unsafe fn extend_lifetime_ref<'c>(&self) -> &'c RenderReturn<'c> {
|
|
unsafe { std::mem::transmute(self) }
|
|
}
|
|
pub(crate) unsafe fn extend_lifetime<'c>(self) -> RenderReturn<'c> {
|
|
unsafe { std::mem::transmute(self) }
|
|
}
|
|
}
|
|
|
|
pub trait IntoDynNode<'a, A = ()> {
|
|
fn into_vnode(self, cx: &'a ScopeState) -> DynamicNode<'a>;
|
|
}
|
|
|
|
impl<'a, 'b> IntoDynNode<'a> for () {
|
|
fn into_vnode(self, _cx: &'a ScopeState) -> DynamicNode<'a> {
|
|
DynamicNode::Fragment(VFragment::Empty(Cell::new(ElementId(0))))
|
|
}
|
|
}
|
|
impl<'a, 'b> IntoDynNode<'a> for VNode<'a> {
|
|
fn into_vnode(self, _cx: &'a ScopeState) -> DynamicNode<'a> {
|
|
DynamicNode::Fragment(VFragment::NonEmpty(_cx.bump().alloc([self])))
|
|
}
|
|
}
|
|
|
|
impl<'a, 'b, T: IntoDynNode<'a>> IntoDynNode<'a> for Option<T> {
|
|
fn into_vnode(self, _cx: &'a ScopeState) -> DynamicNode<'a> {
|
|
match self {
|
|
Some(val) => val.into_vnode(_cx),
|
|
None => DynamicNode::Fragment(VFragment::Empty(Cell::new(ElementId(0)))),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<'a> IntoDynNode<'a> for &Element<'a> {
|
|
fn into_vnode(self, _cx: &'a ScopeState) -> DynamicNode<'a> {
|
|
match self.as_ref() {
|
|
Ok(val) => val.clone().into_vnode(_cx),
|
|
_ => DynamicNode::Fragment(VFragment::Empty(Cell::new(ElementId(0)))),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<'a, 'b> IntoDynNode<'a> for LazyNodes<'a, 'b> {
|
|
fn into_vnode(self, cx: &'a ScopeState) -> DynamicNode<'a> {
|
|
DynamicNode::Fragment(VFragment::NonEmpty(cx.bump().alloc([self.call(cx)])))
|
|
}
|
|
}
|
|
|
|
impl<'b> IntoDynNode<'_> for &'b str {
|
|
fn into_vnode(self, cx: &ScopeState) -> DynamicNode {
|
|
cx.text(format_args!("{}", self))
|
|
}
|
|
}
|
|
|
|
impl IntoDynNode<'_> for String {
|
|
fn into_vnode(self, cx: &ScopeState) -> DynamicNode {
|
|
cx.text(format_args!("{}", self))
|
|
}
|
|
}
|
|
|
|
impl<'b> IntoDynNode<'b> for Arguments<'_> {
|
|
fn into_vnode(self, cx: &'b ScopeState) -> DynamicNode<'b> {
|
|
cx.text(self)
|
|
}
|
|
}
|
|
|
|
impl<'a, 'b> IntoDynNode<'a> for &VNode<'a> {
|
|
fn into_vnode(self, _cx: &'a ScopeState) -> DynamicNode<'a> {
|
|
todo!()
|
|
// VNode {
|
|
// node_id: self.node_id.clone(),
|
|
// parent: self.parent,
|
|
// template: self.template,
|
|
// root_ids: self.root_ids,
|
|
// key: self.key,
|
|
// dynamic_nodes: self.dynamic_nodes,
|
|
// dynamic_attrs: self.dynamic_attrs,
|
|
// }
|
|
}
|
|
}
|
|
|
|
pub trait IntoTemplate<'a> {
|
|
fn into_template(self, _cx: &'a ScopeState) -> VNode<'a>;
|
|
}
|
|
impl<'a, 'b> IntoTemplate<'a> for VNode<'a> {
|
|
fn into_template(self, _cx: &'a ScopeState) -> VNode<'a> {
|
|
self
|
|
}
|
|
}
|
|
impl<'a, 'b> IntoTemplate<'a> for LazyNodes<'a, 'b> {
|
|
fn into_template(self, cx: &'a ScopeState) -> VNode<'a> {
|
|
self.call(cx)
|
|
}
|
|
}
|
|
|
|
// Note that we're using the E as a generic but this is never crafted anyways.
|
|
pub struct FromNodeIterator;
|
|
impl<'a, T, I> IntoDynNode<'a, FromNodeIterator> for T
|
|
where
|
|
T: Iterator<Item = I>,
|
|
I: IntoTemplate<'a>,
|
|
{
|
|
fn into_vnode(self, cx: &'a ScopeState) -> DynamicNode<'a> {
|
|
let mut nodes = bumpalo::collections::Vec::new_in(cx.bump());
|
|
|
|
for node in self {
|
|
nodes.push(node.into_template(cx));
|
|
}
|
|
|
|
let children = nodes.into_bump_slice();
|
|
|
|
match children.len() {
|
|
0 => DynamicNode::Fragment(VFragment::Empty(Cell::new(ElementId(0)))),
|
|
_ => DynamicNode::Fragment(VFragment::NonEmpty(children)),
|
|
}
|
|
}
|
|
}
|
|
|
|
/// A value that can be converted into an attribute value
|
|
pub trait IntoAttributeValue<'a> {
|
|
/// Convert into an attribute value
|
|
fn into_value(self, bump: &'a Bump) -> AttributeValue<'a>;
|
|
}
|
|
|
|
impl<'a> IntoAttributeValue<'a> for &'a str {
|
|
fn into_value(self, _: &'a Bump) -> AttributeValue<'a> {
|
|
AttributeValue::Text(self)
|
|
}
|
|
}
|
|
impl<'a> IntoAttributeValue<'a> for f32 {
|
|
fn into_value(self, _: &'a Bump) -> AttributeValue<'a> {
|
|
AttributeValue::Float(self)
|
|
}
|
|
}
|
|
impl<'a> IntoAttributeValue<'a> for i32 {
|
|
fn into_value(self, _: &'a Bump) -> AttributeValue<'a> {
|
|
AttributeValue::Int(self)
|
|
}
|
|
}
|
|
impl<'a> IntoAttributeValue<'a> for bool {
|
|
fn into_value(self, _: &'a Bump) -> AttributeValue<'a> {
|
|
AttributeValue::Bool(self)
|
|
}
|
|
}
|
|
impl<'a> IntoAttributeValue<'a> for Arguments<'_> {
|
|
fn into_value(self, bump: &'a Bump) -> AttributeValue<'a> {
|
|
use bumpalo::core_alloc::fmt::Write;
|
|
let mut str_buf = bumpalo::collections::String::new_in(bump);
|
|
str_buf.write_fmt(self).unwrap();
|
|
AttributeValue::Text(str_buf.into_bump_str())
|
|
}
|
|
}
|