Skip to main content

hypen_engine/reconcile/
tree.rs

1use super::resolve::resolve_props;
2use crate::ir::{Element, NodeId, IRNode, Props};
3use indexmap::IndexMap;
4use slotmap::SlotMap;
5use std::sync::Arc;
6
7/// The kind of control flow node for re-reconciliation
8#[derive(Debug, Clone)]
9pub enum ControlFlowKind {
10    /// ForEach iteration container
11    ForEach {
12        item_name: String,
13        key_path: Option<String>,
14    },
15    /// Conditional (When/If) container
16    Conditional,
17}
18
19/// Instance node - a concrete instance of an element in the tree
20///
21/// Uses im::Vector for children to enable O(1) structural sharing during clones.
22/// This is critical for reconciliation performance where nodes are frequently cloned.
23#[derive(Debug, Clone)]
24pub struct InstanceNode {
25    /// Unique node ID
26    pub id: NodeId,
27
28    /// Element type (e.g., "Column", "Text", "__ForEach", "__Conditional")
29    pub element_type: String,
30
31    /// Resolved props (bindings evaluated to actual values)
32    pub props: IndexMap<String, serde_json::Value>,
33
34    /// Raw props (including bindings) for change detection - Arc-wrapped for O(1) clone
35    pub raw_props: Props,
36
37    /// Original element template (for List re-rendering) - legacy
38    /// Only populated for List elements that need to re-render children
39    /// Arc-wrapped for O(1) clone during reconciliation
40    pub element_template: Option<Arc<Element>>,
41
42    /// Original IRNode template (for ForEach/Conditional re-rendering)
43    /// Used for control flow nodes that need to re-render on state change
44    /// Arc-wrapped for O(1) clone during reconciliation
45    pub ir_node_template: Option<Arc<IRNode>>,
46
47    /// Control flow metadata for ForEach/Conditional nodes
48    pub control_flow: Option<ControlFlowKind>,
49
50    // Event handling removed - now done at renderer level
51
52    /// Optional key for reconciliation
53    pub key: Option<String>,
54
55    /// Parent node ID
56    pub parent: Option<NodeId>,
57
58    /// Child node IDs (ordered) - uses im::Vector for O(1) clone
59    pub children: im::Vector<NodeId>,
60}
61
62impl InstanceNode {
63    pub fn new(id: NodeId, element: &Element, state: &serde_json::Value) -> Self {
64        let props = resolve_props(&element.props, state);
65
66        Self {
67            id,
68            element_type: element.element_type.clone(),
69            props,
70            raw_props: element.props.clone(),
71            element_template: None,
72            ir_node_template: None,
73            control_flow: None,
74            key: element.key.clone(),
75            parent: None,
76            children: im::Vector::new(),
77        }
78    }
79
80    /// Create a control flow container node (ForEach or Conditional)
81    pub fn new_control_flow(
82        id: NodeId,
83        element_type: &str,
84        props: IndexMap<String, serde_json::Value>,
85        raw_props: Props,
86        control_flow: ControlFlowKind,
87        ir_node_template: IRNode,
88    ) -> Self {
89        Self {
90            id,
91            element_type: element_type.to_string(),
92            props,
93            raw_props,
94            element_template: None,
95            ir_node_template: Some(Arc::new(ir_node_template)),
96            control_flow: Some(control_flow),
97            key: None,
98            parent: None,
99            children: im::Vector::new(),
100        }
101    }
102
103    /// Update props by re-evaluating bindings against new state
104    pub fn update_props(&mut self, state: &serde_json::Value) {
105        self.props = resolve_props(&self.raw_props, state);
106    }
107
108    /// Check if this is a ForEach control flow node
109    pub fn is_foreach(&self) -> bool {
110        matches!(self.control_flow, Some(ControlFlowKind::ForEach { .. }))
111    }
112
113    /// Check if this is a Conditional control flow node
114    pub fn is_conditional(&self) -> bool {
115        matches!(self.control_flow, Some(ControlFlowKind::Conditional))
116    }
117}
118
119/// The instance tree - maintains the current UI tree state
120pub struct InstanceTree {
121    /// All nodes in the tree
122    nodes: SlotMap<NodeId, InstanceNode>,
123
124    /// Root node ID
125    root: Option<NodeId>,
126}
127
128impl InstanceTree {
129    pub fn new() -> Self {
130        Self {
131            nodes: SlotMap::with_key(),
132            root: None,
133        }
134    }
135
136    /// Clear all nodes and reset the tree
137    pub fn clear(&mut self) {
138        self.nodes.clear();
139        self.root = None;
140    }
141
142    /// Create a new node and return its ID
143    pub fn create_node(&mut self, element: &Element, state: &serde_json::Value) -> NodeId {
144        
145        self.nodes.insert_with_key(|id| InstanceNode::new(id, element, state))
146    }
147
148    /// Create a control flow node (ForEach or Conditional) and return its ID
149    pub fn create_control_flow_node(
150        &mut self,
151        element_type: &str,
152        props: IndexMap<String, serde_json::Value>,
153        raw_props: Props,
154        control_flow: ControlFlowKind,
155        ir_node_template: IRNode,
156    ) -> NodeId {
157        self.nodes.insert_with_key(|id| {
158            InstanceNode::new_control_flow(id, element_type, props, raw_props, control_flow, ir_node_template)
159        })
160    }
161
162    /// Get a node by ID
163    pub fn get(&self, id: NodeId) -> Option<&InstanceNode> {
164        self.nodes.get(id)
165    }
166
167    /// Get a mutable node by ID
168    pub fn get_mut(&mut self, id: NodeId) -> Option<&mut InstanceNode> {
169        self.nodes.get_mut(id)
170    }
171
172    /// Remove a node and all its descendants
173    pub fn remove(&mut self, id: NodeId) -> Option<InstanceNode> {
174        if let Some(node) = self.nodes.get(id) {
175            let children = node.children.clone();
176            // Remove all children recursively
177            for child_id in children {
178                self.remove(child_id);
179            }
180        }
181        self.nodes.remove(id)
182    }
183
184    /// Set the root node
185    pub fn set_root(&mut self, id: NodeId) {
186        self.root = Some(id);
187    }
188
189    /// Get the root node ID
190    pub fn root(&self) -> Option<NodeId> {
191        self.root
192    }
193
194    /// Add a child to a parent node
195    pub fn add_child(&mut self, parent_id: NodeId, child_id: NodeId, before: Option<NodeId>) {
196        if let Some(parent) = self.nodes.get_mut(parent_id) {
197            if let Some(before_id) = before {
198                if let Some(pos) = parent.children.iter().position(|&id| id == before_id) {
199                    parent.children.insert(pos, child_id);
200                } else {
201                    parent.children.push_back(child_id);
202                }
203            } else {
204                parent.children.push_back(child_id);
205            }
206        }
207
208        if let Some(child) = self.nodes.get_mut(child_id) {
209            child.parent = Some(parent_id);
210        }
211    }
212
213    /// Remove a child from its parent
214    pub fn remove_child(&mut self, parent_id: NodeId, child_id: NodeId) {
215        if let Some(parent) = self.nodes.get_mut(parent_id) {
216            parent.children = parent.children.iter().filter(|&&id| id != child_id).copied().collect();
217        }
218
219        if let Some(child) = self.nodes.get_mut(child_id) {
220            child.parent = None;
221        }
222    }
223
224    /// Update all nodes that depend on changed state
225    pub fn update_nodes(&mut self, node_ids: &indexmap::IndexSet<NodeId>, state: &serde_json::Value) {
226        for &node_id in node_ids {
227            if let Some(node) = self.nodes.get_mut(node_id) {
228                node.update_props(state);
229            }
230        }
231    }
232
233    /// Iterate over all nodes
234    pub fn iter(&self) -> impl Iterator<Item = (NodeId, &InstanceNode)> {
235        self.nodes.iter()
236    }
237}
238
239impl Default for InstanceTree {
240    fn default() -> Self {
241        Self::new()
242    }
243}
244
245#[cfg(test)]
246mod tests {
247    
248    use crate::reconcile::resolve::evaluate_binding;
249    use serde_json::json;
250
251    #[test]
252    fn test_evaluate_binding() {
253        use crate::reactive::Binding;
254
255        let state = json!({
256            "user": {
257                "name": "Alice",
258                "age": 30
259            }
260        });
261
262        let name_binding = Binding::state(vec!["user".to_string(), "name".to_string()]);
263        let age_binding = Binding::state(vec!["user".to_string(), "age".to_string()]);
264        let email_binding = Binding::state(vec!["user".to_string(), "email".to_string()]);
265
266        assert_eq!(
267            evaluate_binding(&name_binding, &state),
268            Some(json!("Alice"))
269        );
270        assert_eq!(
271            evaluate_binding(&age_binding, &state),
272            Some(json!(30))
273        );
274        assert_eq!(evaluate_binding(&email_binding, &state), None);
275    }
276}