use crate::innerlude::VProps;
use crate::{any_props::BoxedAnyProps, innerlude::ScopeState};
use crate::{arena::ElementId, Element, Event};
use crate::{
innerlude::{ElementRef, EventHandler, MountId},
properties::ComponentFunction,
};
use crate::{Properties, VirtualDom};
use core::panic;
use std::ops::Deref;
use std::rc::Rc;
use std::vec;
use std::{
any::{Any, TypeId},
cell::Cell,
fmt::{Arguments, Debug},
};
pub type TemplateId = &'static str;
pub enum RenderReturn {
Ready(VNode),
Aborted(VNode),
}
impl Clone for RenderReturn {
fn clone(&self) -> Self {
match self {
RenderReturn::Ready(node) => RenderReturn::Ready(node.clone_mounted()),
RenderReturn::Aborted(node) => RenderReturn::Aborted(node.clone_mounted()),
}
}
}
impl Default for RenderReturn {
fn default() -> Self {
RenderReturn::Aborted(VNode::placeholder())
}
}
impl Deref for RenderReturn {
type Target = VNode;
fn deref(&self) -> &Self::Target {
match self {
RenderReturn::Ready(node) | RenderReturn::Aborted(node) => node,
}
}
}
#[derive(Debug)]
pub(crate) struct VNodeMount {
pub parent: Option<ElementRef>,
pub node: VNode,
pub root_ids: Box<[ElementId]>,
pub(crate) mounted_attributes: Box<[ElementId]>,
pub(crate) mounted_dynamic_nodes: Box<[usize]>,
}
#[derive(Debug)]
pub struct VNodeInner {
pub key: Option<String>,
pub template: Cell<Template>,
pub dynamic_nodes: Box<[DynamicNode]>,
pub dynamic_attrs: Box<[Box<[Attribute]>]>,
}
#[derive(Debug)]
pub struct VNode {
vnode: Rc<VNodeInner>,
pub(crate) mount: Cell<MountId>,
}
impl Clone for VNode {
fn clone(&self) -> Self {
Self {
vnode: self.vnode.clone(),
mount: Default::default(),
}
}
}
impl Drop for VNode {
fn drop(&mut self) {
if Rc::strong_count(&self.vnode) == 1 {
for attrs in self.vnode.dynamic_attrs.iter() {
for attr in attrs.iter() {
if let AttributeValue::Listener(listener) = &attr.value {
listener.callback.recycle();
}
}
}
}
}
}
impl PartialEq for VNode {
fn eq(&self, other: &Self) -> bool {
Rc::ptr_eq(&self.vnode, &other.vnode)
}
}
impl Deref for VNode {
type Target = VNodeInner;
fn deref(&self) -> &Self::Target {
&self.vnode
}
}
impl VNode {
pub(crate) fn clone_mounted(&self) -> Self {
Self {
vnode: self.vnode.clone(),
mount: self.mount.clone(),
}
}
pub fn empty() -> Element {
use std::cell::OnceCell;
thread_local! {
static EMPTY_VNODE: OnceCell<Rc<VNodeInner>> = const { OnceCell::new() };
}
let vnode = EMPTY_VNODE.with(|cell| {
cell.get_or_init(move || {
Rc::new(VNodeInner {
key: None,
dynamic_nodes: Box::new([]),
dynamic_attrs: Box::new([]),
template: Cell::new(Template {
name: "packages/core/nodes.rs:180:0:0",
roots: &[],
node_paths: &[],
attr_paths: &[],
}),
})
})
.clone()
});
Some(Self {
vnode,
mount: Default::default(),
})
}
pub fn placeholder() -> Self {
use std::cell::OnceCell;
thread_local! {
static PLACEHOLDER_VNODE: OnceCell<Rc<VNodeInner>> = const { OnceCell::new() };
}
let vnode = PLACEHOLDER_VNODE.with(|cell| {
cell.get_or_init(move || {
Rc::new(VNodeInner {
key: None,
dynamic_nodes: Box::new([DynamicNode::Placeholder(Default::default())]),
dynamic_attrs: Box::new([]),
template: Cell::new(Template {
name: "packages/core/nodes.rs:198:0:0",
roots: &[TemplateNode::Dynamic { id: 0 }],
node_paths: &[&[]],
attr_paths: &[],
}),
})
})
.clone()
});
Self {
vnode,
mount: Default::default(),
}
}
pub fn new(
key: Option<String>,
template: Template,
dynamic_nodes: Box<[DynamicNode]>,
dynamic_attrs: Box<[Box<[Attribute]>]>,
) -> Self {
Self {
vnode: Rc::new(VNodeInner {
key,
template: Cell::new(template),
dynamic_nodes,
dynamic_attrs,
}),
mount: Default::default(),
}
}
pub fn dynamic_root(&self, idx: usize) -> Option<&DynamicNode> {
match &self.template.get().roots[idx] {
TemplateNode::Element { .. } | TemplateNode::Text { text: _ } => None,
TemplateNode::Dynamic { id } | TemplateNode::DynamicText { id } => {
Some(&self.dynamic_nodes[*id])
}
}
}
pub fn mounted_dynamic_node(
&self,
dynamic_node_idx: usize,
dom: &VirtualDom,
) -> Option<ElementId> {
let mount = self.mount.get().as_usize()?;
match &self.dynamic_nodes[dynamic_node_idx] {
DynamicNode::Text(_) | DynamicNode::Placeholder(_) => dom
.mounts
.get(mount)?
.mounted_dynamic_nodes
.get(dynamic_node_idx)
.map(|id| ElementId(*id)),
_ => None,
}
}
pub fn mounted_root(&self, root_idx: usize, dom: &VirtualDom) -> Option<ElementId> {
let mount = self.mount.get().as_usize()?;
dom.mounts.get(mount)?.root_ids.get(root_idx).copied()
}
pub fn mounted_dynamic_attribute(
&self,
dynamic_attribute_idx: usize,
dom: &VirtualDom,
) -> Option<ElementId> {
let mount = self.mount.get().as_usize()?;
dom.mounts
.get(mount)?
.mounted_attributes
.get(dynamic_attribute_idx)
.copied()
}
}
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone, Copy, PartialEq, Hash, Eq, PartialOrd, Ord)]
pub struct Template {
#[cfg_attr(
feature = "serialize",
serde(deserialize_with = "deserialize_string_leaky")
)]
pub name: &'static str,
#[cfg_attr(feature = "serialize", serde(deserialize_with = "deserialize_leaky"))]
pub roots: &'static [TemplateNode],
#[cfg_attr(
feature = "serialize",
serde(deserialize_with = "deserialize_bytes_leaky")
)]
pub node_paths: &'static [&'static [u8]],
#[cfg_attr(
feature = "serialize",
serde(deserialize_with = "deserialize_bytes_leaky")
)]
pub attr_paths: &'static [&'static [u8]],
}
#[cfg(feature = "serialize")]
fn deserialize_string_leaky<'a, 'de, D>(deserializer: D) -> Result<&'a str, D::Error>
where
D: serde::Deserializer<'de>,
{
use serde::Deserialize;
let deserialized = String::deserialize(deserializer)?;
Ok(&*Box::leak(deserialized.into_boxed_str()))
}
#[cfg(feature = "serialize")]
fn deserialize_bytes_leaky<'a, 'de, D>(deserializer: D) -> Result<&'a [&'a [u8]], D::Error>
where
D: serde::Deserializer<'de>,
{
use serde::Deserialize;
let deserialized = Vec::<Vec<u8>>::deserialize(deserializer)?;
let deserialized = deserialized
.into_iter()
.map(|v| &*Box::leak(v.into_boxed_slice()))
.collect::<Vec<_>>();
Ok(&*Box::leak(deserialized.into_boxed_slice()))
}
#[cfg(feature = "serialize")]
fn deserialize_leaky<'a, 'de, T, D>(deserializer: D) -> Result<&'a [T], D::Error>
where
T: serde::Deserialize<'de>,
D: serde::Deserializer<'de>,
{
use serde::Deserialize;
let deserialized = Box::<[T]>::deserialize(deserializer)?;
Ok(&*Box::leak(deserialized))
}
#[cfg(feature = "serialize")]
fn deserialize_option_leaky<'a, 'de, D>(deserializer: D) -> Result<Option<&'static str>, D::Error>
where
D: serde::Deserializer<'de>,
{
use serde::Deserialize;
let deserialized = Option::<String>::deserialize(deserializer)?;
Ok(deserialized.map(|deserialized| &*Box::leak(deserialized.into_boxed_str())))
}
impl Template {
pub fn is_completely_dynamic(&self) -> bool {
use TemplateNode::*;
self.roots
.iter()
.all(|root| matches!(root, Dynamic { .. } | DynamicText { .. }))
}
pub(crate) fn breadth_first_attribute_paths(
&self,
) -> impl Iterator<Item = (usize, &'static [u8])> {
#[cfg(not(debug_assertions))]
{
self.attr_paths.iter().copied().enumerate()
}
#[cfg(debug_assertions)]
{
sort_bfo(self.attr_paths).into_iter()
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Hash, Eq, PartialOrd, Ord)]
#[cfg_attr(
feature = "serialize",
derive(serde::Serialize, serde::Deserialize),
serde(tag = "type")
)]
pub enum TemplateNode {
Element {
tag: &'static str,
#[cfg_attr(
feature = "serialize",
serde(deserialize_with = "deserialize_option_leaky")
)]
namespace: Option<&'static str>,
#[cfg_attr(feature = "serialize", serde(deserialize_with = "deserialize_leaky"))]
attrs: &'static [TemplateAttribute],
#[cfg_attr(feature = "serialize", serde(deserialize_with = "deserialize_leaky"))]
children: &'static [TemplateNode],
},
Text {
text: &'static str,
},
Dynamic {
id: usize,
},
DynamicText {
id: usize,
},
}
impl TemplateNode {
pub fn dynamic_id(&self) -> Option<usize> {
use TemplateNode::*;
match self {
Dynamic { id } | DynamicText { id } => Some(*id),
_ => None,
}
}
}
#[derive(Debug)]
pub enum DynamicNode {
Component(VComponent),
Text(VText),
Placeholder(VPlaceholder),
Fragment(Vec<VNode>),
}
impl DynamicNode {
pub fn make_node<'c, I>(into: impl IntoDynNode<I> + 'c) -> DynamicNode {
into.into_dyn_node()
}
}
impl Default for DynamicNode {
fn default() -> Self {
Self::Placeholder(Default::default())
}
}
pub struct VComponent {
pub name: &'static str,
pub(crate) render_fn: TypeId,
pub(crate) props: BoxedAnyProps,
}
impl VComponent {
pub fn new<P, M: 'static>(
component: impl ComponentFunction<P, M>,
props: P,
fn_name: &'static str,
) -> Self
where
P: Properties + 'static,
{
let render_fn = component.id();
let props = Box::new(VProps::new(
component,
<P as Properties>::memoize,
props,
fn_name,
));
VComponent {
name: fn_name,
props,
render_fn,
}
}
pub fn mounted_scope<'a>(
&self,
dynamic_node_index: usize,
vnode: &VNode,
dom: &'a VirtualDom,
) -> Option<&'a ScopeState> {
let mount = vnode.mount.get().as_usize()?;
let scope_id = dom.mounts.get(mount)?.mounted_dynamic_nodes[dynamic_node_index];
dom.scopes.get(scope_id)
}
}
impl std::fmt::Debug for VComponent {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("VComponent")
.field("name", &self.name)
.finish()
}
}
#[derive(Clone, Debug)]
pub struct VText {
pub value: String,
}
impl VText {
pub fn new(value: String) -> Self {
Self { value }
}
}
impl From<Arguments<'_>> for VText {
fn from(args: Arguments) -> Self {
Self::new(args.to_string())
}
}
#[derive(Clone, Debug, Default)]
#[non_exhaustive]
pub struct VPlaceholder {}
#[derive(Debug, PartialEq, Hash, Eq, PartialOrd, Ord)]
#[cfg_attr(
feature = "serialize",
derive(serde::Serialize, serde::Deserialize),
serde(tag = "type")
)]
pub enum TemplateAttribute {
Static {
name: &'static str,
value: &'static str,
namespace: Option<&'static str>,
},
Dynamic {
id: usize,
},
}
#[derive(Debug, Clone, PartialEq)]
pub struct Attribute {
pub name: &'static str,
pub value: AttributeValue,
pub namespace: Option<&'static str>,
pub volatile: bool,
}
impl Attribute {
pub fn new(
name: &'static str,
value: impl IntoAttributeValue,
namespace: Option<&'static str>,
volatile: bool,
) -> Attribute {
Attribute {
name,
namespace,
volatile,
value: value.into_value(),
}
}
}
pub enum AttributeValue {
Text(String),
Float(f64),
Int(i64),
Bool(bool),
Listener(ListenerCb),
Any(Box<dyn AnyValue>),
None,
}
impl AttributeValue {
pub fn listener<T: 'static>(mut callback: impl FnMut(Event<T>) + 'static) -> AttributeValue {
AttributeValue::Listener(EventHandler::leak(move |event: Event<dyn Any>| {
let data = event.data.downcast::<T>().unwrap();
callback(Event {
propagates: event.propagates,
data,
});
}))
}
pub fn any_value<T: AnyValue>(value: T) -> AttributeValue {
AttributeValue::Any(Box::new(value))
}
}
pub type ListenerCb = EventHandler<Event<dyn Any>>;
impl std::fmt::Debug for AttributeValue {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Text(arg0) => f.debug_tuple("Text").field(arg0).finish(),
Self::Float(arg0) => f.debug_tuple("Float").field(arg0).finish(),
Self::Int(arg0) => f.debug_tuple("Int").field(arg0).finish(),
Self::Bool(arg0) => f.debug_tuple("Bool").field(arg0).finish(),
Self::Listener(listener) => f.debug_tuple("Listener").field(listener).finish(),
Self::Any(_) => f.debug_tuple("Any").finish(),
Self::None => write!(f, "None"),
}
}
}
impl PartialEq for AttributeValue {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(Self::Text(l0), Self::Text(r0)) => l0 == r0,
(Self::Float(l0), Self::Float(r0)) => l0 == r0,
(Self::Int(l0), Self::Int(r0)) => l0 == r0,
(Self::Bool(l0), Self::Bool(r0)) => l0 == r0,
(Self::Listener(_), Self::Listener(_)) => true,
(Self::Any(l0), Self::Any(r0)) => l0.as_ref().any_cmp(r0.as_ref()),
(Self::None, Self::None) => true,
_ => false,
}
}
}
impl Clone for AttributeValue {
fn clone(&self) -> Self {
match self {
Self::Text(arg0) => Self::Text(arg0.clone()),
Self::Float(arg0) => Self::Float(*arg0),
Self::Int(arg0) => Self::Int(*arg0),
Self::Bool(arg0) => Self::Bool(*arg0),
Self::Listener(_) | Self::Any(_) => panic!("Cannot clone listener or any value"),
Self::None => Self::None,
}
}
}
#[doc(hidden)]
pub trait AnyValue: 'static {
fn any_cmp(&self, other: &dyn AnyValue) -> bool;
fn as_any(&self) -> &dyn Any;
fn type_id(&self) -> TypeId {
self.as_any().type_id()
}
}
impl<T: Any + PartialEq + 'static> AnyValue for T {
fn any_cmp(&self, other: &dyn AnyValue) -> bool {
if let Some(other) = other.as_any().downcast_ref() {
self == other
} else {
false
}
}
fn as_any(&self) -> &dyn Any {
self
}
}
pub trait IntoDynNode<A = ()> {
fn into_dyn_node(self) -> DynamicNode;
}
impl IntoDynNode for () {
fn into_dyn_node(self) -> DynamicNode {
DynamicNode::default()
}
}
impl IntoDynNode for VNode {
fn into_dyn_node(self) -> DynamicNode {
DynamicNode::Fragment(vec![self])
}
}
impl IntoDynNode for DynamicNode {
fn into_dyn_node(self) -> DynamicNode {
self
}
}
impl<T: IntoDynNode> IntoDynNode for Option<T> {
fn into_dyn_node(self) -> DynamicNode {
match self {
Some(val) => val.into_dyn_node(),
None => DynamicNode::default(),
}
}
}
impl IntoDynNode for &Element {
fn into_dyn_node(self) -> DynamicNode {
match self.as_ref() {
Some(val) => val.clone().into_dyn_node(),
_ => DynamicNode::default(),
}
}
}
impl IntoDynNode for &str {
fn into_dyn_node(self) -> DynamicNode {
DynamicNode::Text(VText {
value: self.to_string(),
})
}
}
impl IntoDynNode for String {
fn into_dyn_node(self) -> DynamicNode {
DynamicNode::Text(VText { value: self })
}
}
impl IntoDynNode for Arguments<'_> {
fn into_dyn_node(self) -> DynamicNode {
DynamicNode::Text(VText {
value: self.to_string(),
})
}
}
impl IntoDynNode for &VNode {
fn into_dyn_node(self) -> DynamicNode {
DynamicNode::Fragment(vec![self.clone()])
}
}
pub trait IntoVNode {
fn into_vnode(self) -> VNode;
}
impl IntoVNode for VNode {
fn into_vnode(self) -> VNode {
self
}
}
impl IntoVNode for &VNode {
fn into_vnode(self) -> VNode {
self.clone()
}
}
impl IntoVNode for Element {
fn into_vnode(self) -> VNode {
match self {
Some(val) => val.into_vnode(),
_ => VNode::empty().unwrap(),
}
}
}
impl IntoVNode for &Element {
fn into_vnode(self) -> VNode {
match self {
Some(val) => val.into_vnode(),
_ => VNode::empty().unwrap(),
}
}
}
pub struct FromNodeIterator;
impl<T, I> IntoDynNode<FromNodeIterator> for T
where
T: Iterator<Item = I>,
I: IntoVNode,
{
fn into_dyn_node(self) -> DynamicNode {
let children: Vec<_> = self.into_iter().map(|node| node.into_vnode()).collect();
if children.is_empty() {
DynamicNode::default()
} else {
DynamicNode::Fragment(children)
}
}
}
pub trait IntoAttributeValue {
fn into_value(self) -> AttributeValue;
}
impl IntoAttributeValue for AttributeValue {
fn into_value(self) -> AttributeValue {
self
}
}
impl IntoAttributeValue for &str {
fn into_value(self) -> AttributeValue {
AttributeValue::Text(self.to_string())
}
}
impl IntoAttributeValue for String {
fn into_value(self) -> AttributeValue {
AttributeValue::Text(self)
}
}
impl IntoAttributeValue for f64 {
fn into_value(self) -> AttributeValue {
AttributeValue::Float(self)
}
}
impl IntoAttributeValue for i64 {
fn into_value(self) -> AttributeValue {
AttributeValue::Int(self)
}
}
impl IntoAttributeValue for bool {
fn into_value(self) -> AttributeValue {
AttributeValue::Bool(self)
}
}
impl IntoAttributeValue for Arguments<'_> {
fn into_value(self) -> AttributeValue {
AttributeValue::Text(self.to_string())
}
}
impl IntoAttributeValue for Box<dyn AnyValue> {
fn into_value(self) -> AttributeValue {
AttributeValue::Any(self)
}
}
impl<T: IntoAttributeValue> IntoAttributeValue for Option<T> {
fn into_value(self) -> AttributeValue {
match self {
Some(val) => val.into_value(),
None => AttributeValue::None,
}
}
}
pub trait HasAttributes {
fn push_attribute(
self,
name: &'static str,
ns: Option<&'static str>,
attr: impl IntoAttributeValue,
volatile: bool,
) -> Self;
}
#[cfg(debug_assertions)]
pub(crate) fn sort_bfo(paths: &[&'static [u8]]) -> Vec<(usize, &'static [u8])> {
let mut with_indecies = paths.iter().copied().enumerate().collect::<Vec<_>>();
with_indecies.sort_unstable_by(|(_, a), (_, b)| {
let mut a = a.iter();
let mut b = b.iter();
loop {
match (a.next(), b.next()) {
(Some(a), Some(b)) => {
if a != b {
return a.cmp(b);
}
}
(None, Some(_)) => return std::cmp::Ordering::Less,
(Some(_), None) => return std::cmp::Ordering::Greater,
(None, None) => return std::cmp::Ordering::Equal,
}
}
});
with_indecies
}
#[test]
#[cfg(debug_assertions)]
fn sorting() {
let r: [(usize, &[u8]); 5] = [
(0, &[0, 1]),
(1, &[0, 2]),
(2, &[1, 0]),
(3, &[1, 0, 1]),
(4, &[1, 2]),
];
assert_eq!(
sort_bfo(&[&[0, 1,], &[0, 2,], &[1, 0,], &[1, 0, 1,], &[1, 2,],]),
r
);
let r: [(usize, &[u8]); 6] = [
(0, &[0]),
(1, &[0, 1]),
(2, &[0, 1, 2]),
(3, &[1]),
(4, &[1, 2]),
(5, &[2]),
];
assert_eq!(
sort_bfo(&[&[0], &[0, 1], &[0, 1, 2], &[1], &[1, 2], &[2],]),
r
);
}