on 73ddeb8 help sign in
dodrio/src/node.rs
223 lines 6.1 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>],
32 pub(crate) namespace: Option<&'a str>,
35/// An event listener callback function.
36///
37/// It takes three parameters:
38///
39/// 1. The virtual DOM's root rendering component.
40/// 2. A capability to scheduler virtual DOM re-rendering.
41/// 3. The event that occurred.
42pub type ListenerCallback<'a> =
43 &'a (dyn Fn(&mut dyn RootRender, VdomWeak, web_sys::Event) + 'static);
45/// An event listener.
46pub struct Listener<'a> {
47 /// The type of event to listen for.
48 pub(crate) event: &'a str,
49 /// The callback to invoke when the event happens.
50 pub(crate) callback: ListenerCallback<'a>,
53/// An attribute on a DOM node, such as `id="my-thing"` or
54/// `href="https://example.com"`.
55#[derive(Clone, Debug)]
56pub struct Attribute<'a> {
57 pub(crate) name: &'a str,
58 pub(crate) value: &'a str,
61impl fmt::Debug for Listener<'_> {
62 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
63 let (a, b) = self.get_callback_parts();
64 let a = a as *mut u32;
65 let b = b as *mut u32;
66 f.debug_struct("Listener")
67 .field("event", &self.event)
68 .field("callback", &(a, b))
69 .finish()
70 }
73impl<'a> Attribute<'a> {
74 /// Get this attribute's name, such as `"id"` in `<div id="my-thing" />`.
75 #[inline]
76 pub fn name(&self) -> &'a str {
77 self.name
78 }
80 /// The attribute value, such as `"my-thing"` in `<div id="my-thing" />`.
81 #[inline]
82 pub fn value(&self) -> &'a str {
83 self.value
84 }
86 /// Certain attributes are considered "volatile" and can change via user
87 /// input that we can't see when diffing against the old virtual DOM. For
88 /// these attributes, we want to always re-set the attribute on the physical
89 /// DOM node, even if the old and new virtual DOM nodes have the same value.
90 #[inline]
91 pub(crate) fn is_volatile(&self) -> bool {
92 match self.name {
93 "value" | "checked" | "selected" => true,
94 _ => false,
95 }
96 }
99impl<'a> Node<'a> {
100 /// Construct a new Node of type element with given tag name and children
101 #[inline]
102 pub(crate) fn element<Listeners, Attributes, Children>(
103 bump: &'a Bump,
104 tag_name: &'a str,
105 listeners: Listeners,
106 attributes: Attributes,
107 children: Children,
108 namespace: Option<&'a str>,
109 ) -> Node<'a>
110 where
111 Listeners: 'a + AsRef<[Listener<'a>]>,
112 Attributes: 'a + AsRef<[Attribute<'a>]>,
113 Children: 'a + AsRef<[Node<'a>]>,
114 {
115 let children: &'a Children = bump.alloc(children);
116 let children: &'a [Node<'a>] = children.as_ref();
118 let listeners: &'a Listeners = bump.alloc(listeners);
119 let listeners: &'a [Listener<'a>] = listeners.as_ref();
121 let attributes: &'a Attributes = bump.alloc(attributes);
122 let attributes: &'a [Attribute<'a>] = attributes.as_ref();
124 Node::Element(ElementNode {
125 tag_name,
126 listeners,
127 attributes,
128 children,
129 namespace,
130 })
131 }
133 /// Is this node a text node?
134 pub fn is_text(&self) -> bool {
135 match self {
136 Node::Text(_) => true,
137 _ => false,
138 }
139 }
141 /// Is this node an element?
142 pub fn is_element(&self) -> bool {
143 match self {
144 Node::Element { .. } => true,
145 _ => false,
146 }
147 }
149 /// Construct a new text node with the given text.
150 #[inline]
151 pub(crate) fn text(text: &'a str) -> Node<'a> {
152 Node::Text(TextNode { text })
153 }
156/// A node can become an iterator that yields the node itself once.
157///
158/// This implementation of `IntoIterator` mostly exists to improve the
159/// `typed-html` ergonomics, where the macro invokes `.into_iter()` on the child
160/// contents of a tag. By implementing `IntoIterator` here, we avoid having to
161/// do nasty shenanigans like `<p>vec![$contents]</p>` instead of plain old
162/// `<p>$contents</p>`.
163impl<'a> IntoIterator for Node<'a> {
164 type Item = Node<'a>;
165 type IntoIter = iter::Once<Self::Item>;
167 fn into_iter(self) -> Self::IntoIter {
168 iter::once(self)
169 }
172impl<'a> TextNode<'a> {
173 /// Get this text node's text content.
174 pub fn text(&self) -> &'a str {
175 self.text
176 }
179impl<'a> ElementNode<'a> {
180 /// Get this element's tag name.
181 pub fn tag_name(&self) -> &'a str {
182 self.tag_name
183 }
185 /// Get this element's attributes.
186 pub fn attributes(&self) -> &'a [Attribute<'a>] {
187 self.attributes
188 }
190 /// Get this element's attributes.
191 pub fn children(&self) -> &'a [Node<'a>] {
192 self.children
193 }
195 /// Get this element's namespace.
196 pub fn namespace(&self) -> Option<&'a str> {
197 self.namespace
198 }
201union CallbackFatPtr<'a> {
202 callback: ListenerCallback<'a>,
203 parts: (u32, u32),
206impl Listener<'_> {
207 #[inline]
208 pub(crate) fn get_callback_parts(&self) -> (u32, u32) {
209 assert_eq!(
210 mem::size_of::<ListenerCallback>(),
211 mem::size_of::<CallbackFatPtr>()
212 );
214 unsafe {
215 let fat = CallbackFatPtr {
216 callback: self.callback,
217 };
218 let (a, b) = fat.parts;
219 debug_assert!(a != 0);
220 (a, b)
221 }
222 }