dioxus_core/
mutations.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
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
use crate::{arena::ElementId, AttributeValue, Template};

/// Something that can handle the mutations that are generated by the diffing process and apply them to the Real DOM
///
/// This object provides a bunch of important information for a renderer to use patch the Real Dom with the state of the
/// VirtualDom. This includes the scopes that were modified, the templates that were discovered, and a list of changes
/// in the form of a [`Mutation`].
///
/// These changes are specific to one subtree, so to patch multiple subtrees, you'd need to handle each set separately.
///
/// Templates, however, apply to all subtrees, not just target subtree.
///
/// Mutations are the only link between the RealDOM and the VirtualDOM.
pub trait WriteMutations {
    /// Add these m children to the target element
    ///
    /// Id: The ID of the element being mounted to
    /// M: The number of nodes on the stack to append to the target element
    fn append_children(&mut self, id: ElementId, m: usize);

    /// Assign the element at the given path the target ElementId.
    ///
    /// The path is in the form of a list of indices based on children. Templates cannot have more than 255 children per
    /// element, hence the use of a single byte.
    ///
    /// Path: The path of the child of the topmost node on the stack. A path of `[]` represents the topmost node. A path of `[0]` represents the first child. `[0,1,2]` represents 1st child's 2nd child's 3rd child.
    /// Id: The ID we're assigning to this element/placeholder. This will be used later to modify the element or replace it with another element.
    fn assign_node_id(&mut self, path: &'static [u8], id: ElementId);

    /// Create a placeholder in the DOM that we will use later.
    ///
    /// Dioxus currently requires the use of placeholders to maintain a re-entrance point for things like list diffing
    ///
    /// Id: The ID we're assigning to this element/placeholder. This will be used later to modify the element or replace it with another element.
    fn create_placeholder(&mut self, id: ElementId);

    /// Create a node specifically for text with the given value
    ///
    /// Value: The text content of this text node
    /// Id: The ID we're assigning to this specific text nodes. This will be used later to modify the element or replace it with another element.
    fn create_text_node(&mut self, value: &str, id: ElementId);

    /// Load and clone an existing node from a template saved under that specific name
    ///
    /// Dioxus guarantees that the renderer will have already been provided the template.
    /// When the template is picked up in the template list, it should be saved under its "name" - here, the name
    ///
    /// Name: The unique "name" of the template based on the template location. When paired with `rsx!`, this is autogenerated
    /// Index: The index root we loading from the template. The template is stored as a list of nodes. This index represents the position of that root
    /// Id: The ID we're assigning to this element being loaded from the template (This will be used later to move the element around in lists)
    fn load_template(&mut self, template: Template, index: usize, id: ElementId);

    /// Replace the target element (given by its ID) with the topmost m nodes on the stack
    ///
    /// id: The ID of the node we're going to replace with new nodes
    /// m: The number of nodes on the stack to replace the target element with
    fn replace_node_with(&mut self, id: ElementId, m: usize);

    /// Replace an existing element in the template at the given path with the m nodes on the stack
    ///
    /// Path: The path of the child of the topmost node on the stack. A path of `[]` represents the topmost node. A path of `[0]` represents the first child. `[0,1,2]` represents 1st child's 2nd child's 3rd child.
    /// M: The number of nodes on the stack to replace the target element with
    fn replace_placeholder_with_nodes(&mut self, path: &'static [u8], m: usize);

    /// Insert a number of nodes after a given node.
    ///
    /// Id: The ID of the node to insert after.
    /// M: The number of nodes on the stack to insert after the target node.
    fn insert_nodes_after(&mut self, id: ElementId, m: usize);

    /// Insert a number of nodes before a given node.
    ///
    /// Id: The ID of the node to insert before.
    /// M: The number of nodes on the stack to insert before the target node.
    fn insert_nodes_before(&mut self, id: ElementId, m: usize);

    /// Set the value of a node's attribute.
    ///
    /// Name: The name of the attribute to set.
    /// NS: The (optional) namespace of the attribute. For instance, "style" is in the "style" namespace.
    /// Value: The value of the attribute.
    /// Id: The ID of the node to set the attribute of.
    fn set_attribute(
        &mut self,
        name: &'static str,
        ns: Option<&'static str>,
        value: &AttributeValue,
        id: ElementId,
    );

    /// Set the text content of a node.
    ///
    /// Value: The textcontent of the node
    /// Id: The ID of the node to set the textcontent of.
    fn set_node_text(&mut self, value: &str, id: ElementId);

    /// Create a new Event Listener.
    ///
    /// Name: The name of the event to listen for.
    /// Id: The ID of the node to attach the listener to.
    fn create_event_listener(&mut self, name: &'static str, id: ElementId);

    /// Remove an existing Event Listener.
    ///
    /// Name: The name of the event to remove.
    /// Id: The ID of the node to remove.
    fn remove_event_listener(&mut self, name: &'static str, id: ElementId);

    /// Remove a particular node from the DOM
    ///
    /// Id: The ID of the node to remove.
    fn remove_node(&mut self, id: ElementId);

    /// Push the given root node onto our stack.
    ///
    /// Id: The ID of the root node to push.
    fn push_root(&mut self, id: ElementId);
}

/// A `Mutation` represents a single instruction for the renderer to use to modify the UI tree to match the state
/// of the Dioxus VirtualDom.
///
/// These edits can be serialized and sent over the network or through any interface
#[derive(Debug, PartialEq)]
pub enum Mutation {
    /// Add these m children to the target element
    AppendChildren {
        /// The ID of the element being mounted to
        id: ElementId,

        /// The number of nodes on the stack to append to the target element
        m: usize,
    },

    /// Assign the element at the given path the target ElementId.
    ///
    /// The path is in the form of a list of indices based on children. Templates cannot have more than 255 children per
    /// element, hence the use of a single byte.
    AssignId {
        /// The path of the child of the topmost node on the stack
        ///
        /// A path of `[]` represents the topmost node. A path of `[0]` represents the first child.
        /// `[0,1,2]` represents 1st child's 2nd child's 3rd child.
        path: &'static [u8],

        /// The ID we're assigning to this element/placeholder.
        ///
        /// This will be used later to modify the element or replace it with another element.
        id: ElementId,
    },

    /// Create a placeholder in the DOM that we will use later.
    ///
    /// Dioxus currently requires the use of placeholders to maintain a re-entrance point for things like list diffing
    CreatePlaceholder {
        /// The ID we're assigning to this element/placeholder.
        ///
        /// This will be used later to modify the element or replace it with another element.
        id: ElementId,
    },

    /// Create a node specifically for text with the given value
    CreateTextNode {
        /// The text content of this text node
        value: String,

        /// The ID we're assigning to this specific text nodes
        ///
        /// This will be used later to modify the element or replace it with another element.
        id: ElementId,
    },

    /// Load and clone an existing node from a template with a given ID
    ///
    /// Dioxus guarantees that the renderer will have already been provided the template.
    /// When the template is picked up in the template list, it should be saved under its "name" - here, the name
    LoadTemplate {
        /// Which root are we loading from the template?
        ///
        /// The template is stored as a list of nodes. This index represents the position of that root
        index: usize,

        /// The ID we're assigning to this element being loaded from the template
        ///
        /// This will be used later to move the element around in lists
        id: ElementId,
    },

    /// Replace the target element (given by its ID) with the topmost m nodes on the stack
    ReplaceWith {
        /// The ID of the node we're going to replace with
        id: ElementId,

        /// The number of nodes on the stack to replace the target element with
        m: usize,
    },

    /// Replace an existing element in the template at the given path with the m nodes on the stack
    ReplacePlaceholder {
        /// The path of the child of the topmost node on the stack
        ///
        /// A path of `[]` represents the topmost node. A path of `[0]` represents the first child.
        /// `[0,1,2]` represents 1st child's 2nd child's 3rd child.
        path: &'static [u8],

        /// The number of nodes on the stack to replace the target element with
        m: usize,
    },

    /// Insert a number of nodes after a given node.
    InsertAfter {
        /// The ID of the node to insert after.
        id: ElementId,

        /// The number of nodes on the stack to insert after the target node.
        m: usize,
    },

    /// Insert a number of nodes before a given node.
    InsertBefore {
        /// The ID of the node to insert before.
        id: ElementId,

        /// The number of nodes on the stack to insert before the target node.
        m: usize,
    },

    /// Set the value of a node's attribute.
    SetAttribute {
        /// The name of the attribute to set.
        name: &'static str,

        /// The (optional) namespace of the attribute.
        /// For instance, "style" is in the "style" namespace.
        ns: Option<&'static str>,

        /// The value of the attribute.
        value: AttributeValue,

        /// The ID of the node to set the attribute of.
        id: ElementId,
    },

    /// Set the textcontent of a node.
    SetText {
        /// The textcontent of the node
        value: String,

        /// The ID of the node to set the textcontent of.
        id: ElementId,
    },

    /// Create a new Event Listener.
    NewEventListener {
        /// The name of the event to listen for.
        name: String,

        /// The ID of the node to attach the listener to.
        id: ElementId,
    },

    /// Remove an existing Event Listener.
    RemoveEventListener {
        /// The name of the event to remove.
        name: String,

        /// The ID of the node to remove.
        id: ElementId,
    },

    /// Remove a particular node from the DOM
    Remove {
        /// The ID of the node to remove.
        id: ElementId,
    },

    /// Push the given root node onto our stack.
    PushRoot {
        /// The ID of the root node to push.
        id: ElementId,
    },
}

/// A static list of mutations that can be applied to the DOM. Note: this list does not contain any `Any` attribute values
#[derive(Debug, PartialEq, Default)]
pub struct Mutations {
    /// Any mutations required to patch the renderer to match the layout of the VirtualDom
    pub edits: Vec<Mutation>,
}

impl WriteMutations for Mutations {
    fn append_children(&mut self, id: ElementId, m: usize) {
        self.edits.push(Mutation::AppendChildren { id, m })
    }

    fn assign_node_id(&mut self, path: &'static [u8], id: ElementId) {
        self.edits.push(Mutation::AssignId { path, id })
    }

    fn create_placeholder(&mut self, id: ElementId) {
        self.edits.push(Mutation::CreatePlaceholder { id })
    }

    fn create_text_node(&mut self, value: &str, id: ElementId) {
        self.edits.push(Mutation::CreateTextNode {
            value: value.into(),
            id,
        })
    }

    fn load_template(&mut self, _template: Template, index: usize, id: ElementId) {
        self.edits.push(Mutation::LoadTemplate { index, id })
    }

    fn replace_node_with(&mut self, id: ElementId, m: usize) {
        self.edits.push(Mutation::ReplaceWith { id, m })
    }

    fn replace_placeholder_with_nodes(&mut self, path: &'static [u8], m: usize) {
        self.edits.push(Mutation::ReplacePlaceholder { path, m })
    }

    fn insert_nodes_after(&mut self, id: ElementId, m: usize) {
        self.edits.push(Mutation::InsertAfter { id, m })
    }

    fn insert_nodes_before(&mut self, id: ElementId, m: usize) {
        self.edits.push(Mutation::InsertBefore { id, m })
    }

    fn set_attribute(
        &mut self,
        name: &'static str,
        ns: Option<&'static str>,
        value: &AttributeValue,
        id: ElementId,
    ) {
        self.edits.push(Mutation::SetAttribute {
            name,
            ns,
            value: match value {
                AttributeValue::Text(s) => AttributeValue::Text(s.clone()),
                AttributeValue::Bool(b) => AttributeValue::Bool(*b),
                AttributeValue::Float(n) => AttributeValue::Float(*n),
                AttributeValue::Int(n) => AttributeValue::Int(*n),
                AttributeValue::None => AttributeValue::None,
                _ => panic!("Cannot serialize attribute value"),
            },
            id,
        })
    }

    fn set_node_text(&mut self, value: &str, id: ElementId) {
        self.edits.push(Mutation::SetText {
            value: value.into(),
            id,
        })
    }

    fn create_event_listener(&mut self, name: &'static str, id: ElementId) {
        self.edits.push(Mutation::NewEventListener {
            name: name.into(),
            id,
        })
    }

    fn remove_event_listener(&mut self, name: &'static str, id: ElementId) {
        self.edits.push(Mutation::RemoveEventListener {
            name: name.into(),
            id,
        })
    }

    fn remove_node(&mut self, id: ElementId) {
        self.edits.push(Mutation::Remove { id })
    }

    fn push_root(&mut self, id: ElementId) {
        self.edits.push(Mutation::PushRoot { id })
    }
}

/// A struct that ignores all mutations
pub struct NoOpMutations;

impl WriteMutations for NoOpMutations {
    fn append_children(&mut self, _: ElementId, _: usize) {}

    fn assign_node_id(&mut self, _: &'static [u8], _: ElementId) {}

    fn create_placeholder(&mut self, _: ElementId) {}

    fn create_text_node(&mut self, _: &str, _: ElementId) {}

    fn load_template(&mut self, _: Template, _: usize, _: ElementId) {}

    fn replace_node_with(&mut self, _: ElementId, _: usize) {}

    fn replace_placeholder_with_nodes(&mut self, _: &'static [u8], _: usize) {}

    fn insert_nodes_after(&mut self, _: ElementId, _: usize) {}

    fn insert_nodes_before(&mut self, _: ElementId, _: usize) {}

    fn set_attribute(
        &mut self,
        _: &'static str,
        _: Option<&'static str>,
        _: &AttributeValue,
        _: ElementId,
    ) {
    }

    fn set_node_text(&mut self, _: &str, _: ElementId) {}

    fn create_event_listener(&mut self, _: &'static str, _: ElementId) {}

    fn remove_event_listener(&mut self, _: &'static str, _: ElementId) {}

    fn remove_node(&mut self, _: ElementId) {}

    fn push_root(&mut self, _: ElementId) {}
}