Skip to main content

hypen_engine/reconcile/
tree.rs

1use super::resolve::resolve_props;
2use crate::ir::{Element, IRNode, NodeId, 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    /// Optional key for reconciliation
52    pub key: Option<String>,
53
54    /// Parent node ID
55    pub parent: Option<NodeId>,
56
57    /// Child node IDs (ordered) - uses im::Vector for O(1) clone
58    pub children: im::Vector<NodeId>,
59}
60
61impl InstanceNode {
62    pub fn new(id: NodeId, element: &Element, state: &serde_json::Value) -> Self {
63        let props = resolve_props(&element.props, state);
64
65        Self {
66            id,
67            element_type: element.element_type.clone(),
68            props,
69            raw_props: element.props.clone(),
70            element_template: None,
71            ir_node_template: None,
72            control_flow: None,
73            key: element.key.clone(),
74            parent: None,
75            children: im::Vector::new(),
76        }
77    }
78
79    /// Create a control flow container node (ForEach or Conditional)
80    pub fn new_control_flow(
81        id: NodeId,
82        element_type: &str,
83        props: IndexMap<String, serde_json::Value>,
84        raw_props: Props,
85        control_flow: ControlFlowKind,
86        ir_node_template: IRNode,
87    ) -> Self {
88        Self {
89            id,
90            element_type: element_type.to_string(),
91            props,
92            raw_props,
93            element_template: None,
94            ir_node_template: Some(Arc::new(ir_node_template)),
95            control_flow: Some(control_flow),
96            key: None,
97            parent: None,
98            children: im::Vector::new(),
99        }
100    }
101
102    /// Update props by re-evaluating bindings against new state
103    pub fn update_props(&mut self, state: &serde_json::Value) {
104        self.props = resolve_props(&self.raw_props, state);
105    }
106
107    /// Check if this is a ForEach control flow node
108    pub fn is_foreach(&self) -> bool {
109        matches!(self.control_flow, Some(ControlFlowKind::ForEach { .. }))
110    }
111
112    /// Check if this is a Conditional control flow node
113    pub fn is_conditional(&self) -> bool {
114        matches!(self.control_flow, Some(ControlFlowKind::Conditional))
115    }
116}
117
118/// The instance tree - maintains the current UI tree state
119pub struct InstanceTree {
120    /// All nodes in the tree
121    nodes: SlotMap<NodeId, InstanceNode>,
122
123    /// Root node ID
124    root: Option<NodeId>,
125}
126
127impl InstanceTree {
128    pub fn new() -> Self {
129        Self {
130            nodes: SlotMap::with_key(),
131            root: None,
132        }
133    }
134
135    /// Clear all nodes and reset the tree
136    pub fn clear(&mut self) {
137        self.nodes.clear();
138        self.root = None;
139    }
140
141    /// Create a new node and return its ID
142    pub fn create_node(&mut self, element: &Element, state: &serde_json::Value) -> NodeId {
143        self.nodes
144            .insert_with_key(|id| InstanceNode::new(id, element, state))
145    }
146
147    /// Create a control flow node (ForEach or Conditional) and return its ID
148    pub fn create_control_flow_node(
149        &mut self,
150        element_type: &str,
151        props: IndexMap<String, serde_json::Value>,
152        raw_props: Props,
153        control_flow: ControlFlowKind,
154        ir_node_template: IRNode,
155    ) -> NodeId {
156        self.nodes.insert_with_key(|id| {
157            InstanceNode::new_control_flow(
158                id,
159                element_type,
160                props,
161                raw_props,
162                control_flow,
163                ir_node_template,
164            )
165        })
166    }
167
168    /// Get a node by ID
169    pub fn get(&self, id: NodeId) -> Option<&InstanceNode> {
170        self.nodes.get(id)
171    }
172
173    /// Get a mutable node by ID
174    pub fn get_mut(&mut self, id: NodeId) -> Option<&mut InstanceNode> {
175        self.nodes.get_mut(id)
176    }
177
178    /// Remove a node and all its descendants
179    pub fn remove(&mut self, id: NodeId) -> Option<InstanceNode> {
180        if let Some(node) = self.nodes.get(id) {
181            let children = node.children.clone();
182            // Remove all children recursively
183            for child_id in children {
184                self.remove(child_id);
185            }
186        }
187        self.nodes.remove(id)
188    }
189
190    /// Set the root node
191    pub fn set_root(&mut self, id: NodeId) {
192        self.root = Some(id);
193    }
194
195    /// Get the root node ID
196    pub fn root(&self) -> Option<NodeId> {
197        self.root
198    }
199
200    /// Add a child to a parent node
201    pub fn add_child(&mut self, parent_id: NodeId, child_id: NodeId, before: Option<NodeId>) {
202        if let Some(parent) = self.nodes.get_mut(parent_id) {
203            if let Some(before_id) = before {
204                if let Some(pos) = parent.children.iter().position(|&id| id == before_id) {
205                    parent.children.insert(pos, child_id);
206                } else {
207                    parent.children.push_back(child_id);
208                }
209            } else {
210                parent.children.push_back(child_id);
211            }
212        }
213
214        if let Some(child) = self.nodes.get_mut(child_id) {
215            child.parent = Some(parent_id);
216        }
217    }
218
219    /// Remove a child from its parent
220    pub fn remove_child(&mut self, parent_id: NodeId, child_id: NodeId) {
221        if let Some(parent) = self.nodes.get_mut(parent_id) {
222            parent.children = parent
223                .children
224                .iter()
225                .filter(|&&id| id != child_id)
226                .copied()
227                .collect();
228        }
229
230        if let Some(child) = self.nodes.get_mut(child_id) {
231            child.parent = None;
232        }
233    }
234
235    /// Update all nodes that depend on changed state
236    pub fn update_nodes(
237        &mut self,
238        node_ids: &indexmap::IndexSet<NodeId>,
239        state: &serde_json::Value,
240    ) {
241        for &node_id in node_ids {
242            if let Some(node) = self.nodes.get_mut(node_id) {
243                node.update_props(state);
244            }
245        }
246    }
247
248    /// Iterate over all nodes
249    pub fn iter(&self) -> impl Iterator<Item = (NodeId, &InstanceNode)> {
250        self.nodes.iter()
251    }
252}
253
254impl Default for InstanceTree {
255    fn default() -> Self {
256        Self::new()
257    }
258}
259
260#[cfg(test)]
261mod tests {
262
263    use crate::reconcile::resolve::evaluate_binding;
264    use serde_json::json;
265
266    #[test]
267    fn test_evaluate_binding() {
268        use crate::reactive::Binding;
269
270        let state = json!({
271            "user": {
272                "name": "Alice",
273                "age": 30
274            }
275        });
276
277        let name_binding = Binding::state(vec!["user".to_string(), "name".to_string()]);
278        let age_binding = Binding::state(vec!["user".to_string(), "age".to_string()]);
279        let email_binding = Binding::state(vec!["user".to_string(), "email".to_string()]);
280
281        assert_eq!(
282            evaluate_binding(&name_binding, &state),
283            Some(json!("Alice"))
284        );
285        assert_eq!(evaluate_binding(&age_binding, &state), Some(json!(30)));
286        assert_eq!(evaluate_binding(&email_binding, &state), None);
287    }
288}