Skip to main content

appscale_core/
tree.rs

1//! Shadow Tree — the framework's internal representation of the UI.
2//!
3//! This mirrors React's fiber tree but lives in Rust.
4//! It is the source of truth for:
5//! - What nodes exist
6//! - Parent/child relationships
7//! - Current props for each node
8//! - Native view handle mappings
9
10use crate::platform::{NativeHandle, ViewType, PropsDiff, PropValue};
11use rustc_hash::FxHashMap;
12use std::collections::HashMap;
13
14/// Unique identifier for a node in the shadow tree.
15/// Assigned by the reconciler (JavaScript side) and passed through IR.
16#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
17pub struct NodeId(pub u64);
18
19impl std::fmt::Display for NodeId {
20    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
21        write!(f, "node#{}", self.0)
22    }
23}
24
25/// A single node in the shadow tree.
26#[derive(Debug, Clone)]
27pub struct ShadowNode {
28    pub id: NodeId,
29    pub view_type: ViewType,
30    pub parent: Option<NodeId>,
31    pub children: Vec<NodeId>,
32    pub props: HashMap<String, PropValue>,
33    pub native_handle: Option<NativeHandle>,
34
35    /// Props that changed since last mount — consumed by mount phase.
36    pending_props: PropsDiff,
37
38    /// Component name from React (for DevTools).
39    pub component_name: Option<String>,
40}
41
42/// The shadow tree — owns all nodes.
43pub struct ShadowTree {
44    nodes: FxHashMap<NodeId, ShadowNode>,
45    root: Option<NodeId>,
46}
47
48impl ShadowTree {
49    pub fn new() -> Self {
50        Self {
51            nodes: FxHashMap::default(),
52            root: None,
53        }
54    }
55
56    /// Create a new node. Does NOT insert into the tree hierarchy —
57    /// use `append_child` or `insert_before` for that.
58    pub fn create_node(
59        &mut self,
60        id: NodeId,
61        view_type: ViewType,
62        initial_props: HashMap<String, PropValue>,
63    ) {
64        let pending = PropsDiff {
65            changes: initial_props.clone(),
66        };
67
68        let node = ShadowNode {
69            id,
70            view_type,
71            parent: None,
72            children: Vec::new(),
73            props: initial_props,
74            native_handle: None,
75            pending_props: pending,
76            component_name: None,
77        };
78
79        self.nodes.insert(id, node);
80    }
81
82    /// Set the root node of the tree.
83    pub fn set_root(&mut self, id: NodeId) {
84        self.root = Some(id);
85    }
86
87    /// Get the root node ID.
88    pub fn root(&self) -> Option<NodeId> {
89        self.root
90    }
91
92    /// Get a node by ID (immutable).
93    pub fn get(&self, id: NodeId) -> Option<&ShadowNode> {
94        self.nodes.get(&id)
95    }
96
97    /// Get a node by ID (mutable).
98    pub fn get_mut(&mut self, id: NodeId) -> Option<&mut ShadowNode> {
99        self.nodes.get_mut(&id)
100    }
101
102    /// Update props on a node. Changed props go into pending_props
103    /// for the mount phase to consume.
104    pub fn update_props(&mut self, id: NodeId, diff: &PropsDiff) {
105        if let Some(node) = self.nodes.get_mut(&id) {
106            for (key, value) in &diff.changes {
107                node.props.insert(key.clone(), value.clone());
108                node.pending_props.set(key.clone(), value.clone());
109            }
110        }
111    }
112
113    /// Append a child to a parent node.
114    pub fn append_child(&mut self, parent_id: NodeId, child_id: NodeId) {
115        // Set parent reference on child
116        if let Some(child) = self.nodes.get_mut(&child_id) {
117            child.parent = Some(parent_id);
118        }
119
120        // Add to parent's children list
121        if let Some(parent) = self.nodes.get_mut(&parent_id) {
122            if !parent.children.contains(&child_id) {
123                parent.children.push(child_id);
124            }
125        }
126    }
127
128    /// Insert a child before another child in a parent's children list.
129    pub fn insert_before(
130        &mut self,
131        parent_id: NodeId,
132        child_id: NodeId,
133        before_id: NodeId,
134    ) {
135        if let Some(child) = self.nodes.get_mut(&child_id) {
136            child.parent = Some(parent_id);
137        }
138
139        if let Some(parent) = self.nodes.get_mut(&parent_id) {
140            if let Some(pos) = parent.children.iter().position(|&c| c == before_id) {
141                parent.children.insert(pos, child_id);
142            } else {
143                parent.children.push(child_id);
144            }
145        }
146    }
147
148    /// Remove a child from its parent and the tree.
149    pub fn remove_child(&mut self, parent_id: NodeId, child_id: NodeId) {
150        // Remove from parent's children list
151        if let Some(parent) = self.nodes.get_mut(&parent_id) {
152            parent.children.retain(|&c| c != child_id);
153        }
154
155        // Recursively remove the child and all its descendants
156        self.remove_subtree(child_id);
157    }
158
159    /// Recursively remove a node and all its descendants.
160    fn remove_subtree(&mut self, id: NodeId) {
161        let children = self.nodes.get(&id)
162            .map(|n| n.children.clone())
163            .unwrap_or_default();
164
165        for child_id in children {
166            self.remove_subtree(child_id);
167        }
168
169        self.nodes.remove(&id);
170    }
171
172    /// Set the native handle for a node (after platform bridge creates the view).
173    pub fn set_native_handle(&mut self, id: NodeId, handle: NativeHandle) {
174        if let Some(node) = self.nodes.get_mut(&id) {
175            node.native_handle = Some(handle);
176        }
177    }
178
179    /// Take pending props (moves them out, leaving empty).
180    /// Called by the mount phase after applying to native view.
181    pub fn take_pending_props(&mut self, id: NodeId) -> PropsDiff {
182        self.nodes.get_mut(&id)
183            .map(|n| std::mem::take(&mut n.pending_props))
184            .unwrap_or_default()
185    }
186
187    /// Get the ordered children of a node.
188    pub fn children_of(&self, id: NodeId) -> &[NodeId] {
189        self.nodes.get(&id)
190            .map(|n| n.children.as_slice())
191            .unwrap_or(&[])
192    }
193
194    /// Walk ancestors from a node up to root (inclusive).
195    /// Returns [root, ..., parent, node_id].
196    pub fn ancestors(&self, id: NodeId) -> Vec<NodeId> {
197        let mut path = Vec::new();
198        let mut current = Some(id);
199
200        while let Some(nid) = current {
201            path.push(nid);
202            current = self.nodes.get(&nid).and_then(|n| n.parent);
203        }
204
205        path.reverse();
206        path
207    }
208
209    /// Total number of nodes in the tree.
210    pub fn len(&self) -> usize {
211        self.nodes.len()
212    }
213
214    pub fn is_empty(&self) -> bool {
215        self.nodes.is_empty()
216    }
217
218    /// Iterate all nodes (for DevTools snapshot).
219    pub fn iter(&self) -> impl Iterator<Item = (&NodeId, &ShadowNode)> {
220        self.nodes.iter()
221    }
222}
223
224#[cfg(test)]
225mod tests {
226    use super::*;
227
228    #[test]
229    fn test_create_and_parent() {
230        let mut tree = ShadowTree::new();
231
232        tree.create_node(NodeId(1), ViewType::Container, HashMap::new());
233        tree.create_node(NodeId(2), ViewType::Text, HashMap::new());
234        tree.create_node(NodeId(3), ViewType::Text, HashMap::new());
235
236        tree.set_root(NodeId(1));
237        tree.append_child(NodeId(1), NodeId(2));
238        tree.append_child(NodeId(1), NodeId(3));
239
240        assert_eq!(tree.children_of(NodeId(1)), &[NodeId(2), NodeId(3)]);
241        assert_eq!(tree.get(NodeId(2)).unwrap().parent, Some(NodeId(1)));
242    }
243
244    #[test]
245    fn test_ancestors() {
246        let mut tree = ShadowTree::new();
247
248        tree.create_node(NodeId(1), ViewType::Container, HashMap::new());
249        tree.create_node(NodeId(2), ViewType::Container, HashMap::new());
250        tree.create_node(NodeId(3), ViewType::Text, HashMap::new());
251
252        tree.set_root(NodeId(1));
253        tree.append_child(NodeId(1), NodeId(2));
254        tree.append_child(NodeId(2), NodeId(3));
255
256        let path = tree.ancestors(NodeId(3));
257        assert_eq!(path, vec![NodeId(1), NodeId(2), NodeId(3)]);
258    }
259
260    #[test]
261    fn test_remove_subtree() {
262        let mut tree = ShadowTree::new();
263
264        tree.create_node(NodeId(1), ViewType::Container, HashMap::new());
265        tree.create_node(NodeId(2), ViewType::Container, HashMap::new());
266        tree.create_node(NodeId(3), ViewType::Text, HashMap::new());
267
268        tree.append_child(NodeId(1), NodeId(2));
269        tree.append_child(NodeId(2), NodeId(3));
270
271        tree.remove_child(NodeId(1), NodeId(2));
272
273        assert_eq!(tree.len(), 1); // Only root remains
274        assert!(tree.get(NodeId(2)).is_none());
275        assert!(tree.get(NodeId(3)).is_none());
276    }
277}