on 1189e59 help sign in
dodrio/src/node.rs
208 lines 5.5 kB history raw
1use crate::{RootRender, VdomWeak};
2use bumpalo::Bump;
3use std::fmt;
4use std::iter;
5use std::mem;
7/// A node is either a text node or an element.
8#[derive(Debug, Clone)]
9pub enum Node<'a> {
10 /// A text node.
11 Text(TextNode<'a>),
13 /// An element potentially with attributes and children.
14 Element(ElementNode<'a>),
17/// Text nodes are just a string of text. They cannot have attributes or
18/// children.
19#[derive(Debug, Clone)]
20pub struct TextNode<'a> {
21 pub(crate) text: &'a str,
24/// Elements have a tag name, zero or more attributes, and zero or more
25/// children.
26#[derive(Debug, Clone)]
27pub struct ElementNode<'a> {
28 pub(crate) tag_name: &'a str,
29 pub(crate) listeners: &'a [Listener<'a>],
30 pub(crate) attributes: &'a [Attribute<'a>],
31 pub(crate) children: &'a [Node<'a>],
34/// An event listener callback function.
35///
36/// It takes three parameters:
37///
38/// 1. The virtual DOM's root rendering component.
39/// 2. A capability to scheduler virtual DOM re-rendering.
40/// 3. The event that ocurred.
41pub type ListenerCallback<'a> =
42 &'a (dyn Fn(&mut dyn RootRender, VdomWeak, web_sys::Event) + 'static);
44/// An event listener.
45pub struct Listener<'a> {
46 /// The type of event to listen for.
47 pub(crate) event: &'a str,
48 /// The callback to invoke when the event happens.
49 pub(crate) callback: ListenerCallback<'a>,
52/// An attribute on a DOM node, such as `id="my-thing"` or
53/// `href="https://example.com"`.
54#[derive(Clone, Debug)]
55pub struct Attribute<'a> {
56 pub(crate) name: &'a str,
57 pub(crate) value: &'a str,
60impl fmt::Debug for Listener<'_> {
61 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
62 let (a, b) = self.get_callback_parts();
63 let a = a as *mut u32;
64 let b = b as *mut u32;
65 f.debug_struct("Listener")
66 .field("event", &self.event)
67 .field("callback", &(a, b))
68 .finish()
69 }
72impl<'a> Attribute<'a> {
73 /// Get this attribute's name, such as `"id"` in `<div id="my-thing" />`.
74 #[inline]
75 pub fn name(&self) -> &'a str {
76 self.name
77 }
79 /// The attribute value, such as `"my-thing"` in `<div id="my-thing" />`.
80 #[inline]
81 pub fn value(&self) -> &'a str {
82 self.value
83 }
85 /// Certain attributes are considered "volatile" and can change via user
86 /// input that we can't see when diffing against the old virtual DOM. For
87 /// these attributes, we want to always re-set the attribute on the physical
88 /// DOM node, even if the old and new virtual DOM nodes have the same value.
89 #[inline]
90 pub(crate) fn is_volatile(&self) -> bool {
91 match self.name {
92 "value" | "checked" | "selected" => true,
93 _ => false,
94 }
95 }
98impl<'a> Node<'a> {
99 /// Construct a new element node with the given tag name and children.
100 #[inline]
101 pub(crate) fn element<Listeners, Attributes, Children>(
102 bump: &'a Bump,
103 tag_name: &'a str,
104 listeners: Listeners,
105 attributes: Attributes,
106 children: Children,
107 ) -> Node<'a>
108 where
109 Listeners: 'a + AsRef<[Listener<'a>]>,
110 Attributes: 'a + AsRef<[Attribute<'a>]>,
111 Children: 'a + AsRef<[Node<'a>]>,
112 {
113 let children: &'a Children = bump.alloc(children);
114 let children: &'a [Node<'a>] = children.as_ref();
116 let listeners: &'a Listeners = bump.alloc(listeners);
117 let listeners: &'a [Listener<'a>] = listeners.as_ref();
119 let attributes: &'a Attributes = bump.alloc(attributes);
120 let attributes: &'a [Attribute<'a>] = attributes.as_ref();
122 Node::Element(ElementNode {
123 tag_name,
124 listeners,
125 attributes,
126 children,
127 })
128 }
130 /// Is this node a text node?
131 pub fn is_text(&self) -> bool {
132 match self {
133 Node::Text(_) => true,
134 _ => false,
135 }
136 }
138 /// Is this node an element?
139 pub fn is_element(&self) -> bool {
140 match self {
141 Node::Element { .. } => true,
142 _ => false,
143 }
144 }
146 /// Construct a new text node with the given text.
147 #[inline]
148 pub(crate) fn text(text: &'a str) -> Node<'a> {
149 Node::Text(TextNode { text })
150 }
153impl<'a> IntoIterator for Node<'a> {
154 type Item = Node<'a>;
155 type IntoIter = iter::Once<Self::Item>;
157 fn into_iter(self) -> Self::IntoIter {
158 iter::once(self)
159 }
162impl<'a> TextNode<'a> {
163 /// Get this text node's text content.
164 pub fn text(&self) -> &'a str {
165 self.text
166 }
169impl<'a> ElementNode<'a> {
170 /// Get this element's tag name.
171 pub fn tag_name(&self) -> &'a str {
172 self.tag_name
173 }
175 /// Get this element's attributes.
176 pub fn attributes(&self) -> &'a [Attribute<'a>] {
177 self.attributes
178 }
180 /// Get this element's attributes.
181 pub fn children(&self) -> &'a [Node<'a>] {
182 self.children
183 }
186union CallbackFatPtr<'a> {
187 callback: ListenerCallback<'a>,
188 parts: (u32, u32),
191impl Listener<'_> {
192 #[inline]
193 pub(crate) fn get_callback_parts(&self) -> (u32, u32) {
194 assert_eq!(
195 mem::size_of::<ListenerCallback>(),
196 mem::size_of::<CallbackFatPtr>()
197 );
199 unsafe {
200 let fat = CallbackFatPtr {
201 callback: self.callback,
202 };
203 let (a, b) = fat.parts;
204 debug_assert!(a != 0);
205 (a, b)
206 }
207 }