This repository has been archived by the owner on Mar 2, 2021. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 47
/
node.rs
198 lines (173 loc) · 5.17 KB
/
node.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
use crate::{RootRender, VdomWeak};
use bumpalo::Bump;
use std::fmt;
use std::mem;
/// A node is either a text node or an element.
#[derive(Debug, Clone)]
pub enum Node<'a> {
/// A text node.
Text(TextNode<'a>),
/// An element potentially with attributes and children.
Element(ElementNode<'a>),
}
/// Text nodes are just a string of text. They cannot have attributes or
/// children.
#[derive(Debug, Clone)]
pub struct TextNode<'a> {
pub(crate) text: &'a str,
}
/// Elements have a tag name, zero or more attributes, and zero or more
/// children.
#[derive(Debug, Clone)]
pub struct ElementNode<'a> {
pub(crate) tag_name: &'a str,
pub(crate) listeners: &'a [Listener<'a>],
pub(crate) attributes: &'a [Attribute<'a>],
pub(crate) children: &'a [Node<'a>],
}
/// An event listener callback function.
///
/// It takes three parameters:
///
/// 1. The virtual DOM's root rendering component.
/// 2. A capability to scheduler virtual DOM re-rendering.
/// 3. The event that ocurred.
pub type ListenerCallback<'a> =
&'a (dyn Fn(&mut dyn RootRender, VdomWeak, web_sys::Event) + 'static);
/// An event listener.
pub struct Listener<'a> {
/// The type of event to listen for.
pub(crate) event: &'a str,
/// The callback to invoke when the event happens.
pub(crate) callback: ListenerCallback<'a>,
}
/// An attribute on a DOM node, such as `id="my-thing"` or
/// `href="https://example.com"`.
#[derive(Clone, Debug)]
pub struct Attribute<'a> {
pub(crate) name: &'a str,
pub(crate) value: &'a str,
}
impl fmt::Debug for Listener<'_> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let (a, b) = self.get_callback_parts();
let a = a as *mut u32;
let b = b as *mut u32;
f.debug_struct("Listener")
.field("event", &self.event)
.field("callback", &(a, b))
.finish()
}
}
impl<'a> Attribute<'a> {
/// Get this attribute's name, such as `"id"` in `<div id="my-thing" />`.
#[inline]
pub fn name(&self) -> &'a str {
self.name
}
/// The attribute value, such as `"my-thing"` in `<div id="my-thing" />`.
#[inline]
pub fn value(&self) -> &'a str {
self.value
}
/// Certain attributes are considered "volatile" and can change via user
/// input that we can't see when diffing against the old virtual DOM. For
/// these attributes, we want to always re-set the attribute on the physical
/// DOM node, even if the old and new virtual DOM nodes have the same value.
#[inline]
pub(crate) fn is_volatile(&self) -> bool {
match self.name {
"value" | "checked" | "selected" => true,
_ => false,
}
}
}
impl<'a> Node<'a> {
/// Construct a new element node with the given tag name and children.
#[inline]
pub(crate) fn element<Listeners, Attributes, Children>(
bump: &'a Bump,
tag_name: &'a str,
listeners: Listeners,
attributes: Attributes,
children: Children,
) -> Node<'a>
where
Listeners: 'a + AsRef<[Listener<'a>]>,
Attributes: 'a + AsRef<[Attribute<'a>]>,
Children: 'a + AsRef<[Node<'a>]>,
{
let children: &'a Children = bump.alloc(children);
let children: &'a [Node<'a>] = children.as_ref();
let listeners: &'a Listeners = bump.alloc(listeners);
let listeners: &'a [Listener<'a>] = listeners.as_ref();
let attributes: &'a Attributes = bump.alloc(attributes);
let attributes: &'a [Attribute<'a>] = attributes.as_ref();
Node::Element(ElementNode {
tag_name,
listeners,
attributes,
children,
})
}
/// Is this node a text node?
pub fn is_text(&self) -> bool {
match self {
Node::Text(_) => true,
_ => false,
}
}
/// Is this node an element?
pub fn is_element(&self) -> bool {
match self {
Node::Element { .. } => true,
_ => false,
}
}
/// Construct a new text node with the given text.
#[inline]
pub(crate) fn text(text: &'a str) -> Node<'a> {
Node::Text(TextNode { text })
}
}
impl<'a> TextNode<'a> {
/// Get this text node's text content.
pub fn text(&self) -> &'a str {
self.text
}
}
impl<'a> ElementNode<'a> {
/// Get this element's tag name.
pub fn tag_name(&self) -> &'a str {
self.tag_name
}
/// Get this element's attributes.
pub fn attributes(&self) -> &'a [Attribute<'a>] {
self.attributes
}
/// Get this element's attributes.
pub fn children(&self) -> &'a [Node<'a>] {
self.children
}
}
union CallbackFatPtr<'a> {
callback: ListenerCallback<'a>,
parts: (u32, u32),
}
impl Listener<'_> {
#[inline]
pub(crate) fn get_callback_parts(&self) -> (u32, u32) {
assert_eq!(
mem::size_of::<ListenerCallback>(),
mem::size_of::<CallbackFatPtr>()
);
unsafe {
let fat = CallbackFatPtr {
callback: self.callback,
};
let (a, b) = fat.parts;
debug_assert!(a != 0);
(a, b)
}
}
}