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
use rustc_hash::FxHashSet;

use crate::{arena::ElementId, innerlude::BorrowedAttributeValue, ScopeId, Template};

/// A container for all the relevant steps to modify 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.
#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
#[derive(Debug, Default)]
#[must_use = "not handling edits can lead to visual inconsistencies in UI"]
pub struct Mutations<'a> {
    /// The ID of the subtree that these edits are targetting
    pub subtree: usize,

    /// The list of Scopes that were diffed, created, and removed during the Diff process.
    pub dirty_scopes: FxHashSet<ScopeId>,

    /// Any templates encountered while diffing the DOM.
    ///
    /// These must be loaded into a cache before applying the edits
    pub templates: Vec<Template<'a>>,

    /// Any mutations required to patch the renderer to match the layout of the VirtualDom
    pub edits: Vec<Mutation<'a>>,
}

impl<'a> Mutations<'a> {
    /// Rewrites IDs to just be "template", so you can compare the mutations
    ///
    /// Used really only for testing
    pub fn santize(mut self) -> Self {
        for edit in self.edits.iter_mut() {
            if let Mutation::LoadTemplate { name, .. } = edit {
                *name = "template"
            }
        }

        self
    }

    /// Push a new mutation into the dom_edits list
    pub(crate) fn push(&mut self, mutation: Mutation<'static>) {
        self.edits.push(mutation)
    }
}

/// 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
#[cfg_attr(
    feature = "serialize",
    derive(serde::Serialize, serde::Deserialize),
    serde(tag = "type")
)]
#[derive(Debug, PartialEq)]
pub enum Mutation<'a> {
    /// 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 an placeholder int he 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: &'a str,

        /// 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,
    },

    /// Hydrate an existing text node at the given path with the given text.
    ///
    /// Assign this text node the given ID since we will likely need to modify this text at a later point
    HydrateText {
        /// 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 value of the textnode that we want to set the placeholder with
        value: &'a str,

        /// 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 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
    LoadTemplate {
        /// The "name" of the template. When paired with `rsx!`, this is autogenerated
        name: &'static str,

        /// 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: &'a str,

        /// The value of the attribute.
        value: BorrowedAttributeValue<'a>,

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

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

    /// Set the textcontent of a node.
    SetText {
        /// The textcontent of the node
        value: &'a str,

        /// 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: &'a str,

        /// 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: &'a str,

        /// 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,
    },
}