Files
dioxus/packages/core/src/factory.rs
T
2022-11-22 21:32:26 -08:00

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())
}
}