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> = Box::new(vcomp); let extended: Box = 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> + 'a, { fn as_return(self, cx: &'a ScopeState) -> RenderReturn<'a> { let f: &mut dyn Future> = 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> + '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 { 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, 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()) } }