hypen_engine/reconcile/
tree.rs1use crate::ir::{Element, NodeId, Value};
2use indexmap::IndexMap;
3use slotmap::SlotMap;
4
5#[derive(Debug, Clone)]
7pub struct InstanceNode {
8 pub id: NodeId,
10
11 pub element_type: String,
13
14 pub props: IndexMap<String, serde_json::Value>,
16
17 pub raw_props: IndexMap<String, Value>,
19
20 pub element_template: Option<Element>,
23
24 pub key: Option<String>,
28
29 pub parent: Option<NodeId>,
31
32 pub children: Vec<NodeId>,
34}
35
36impl InstanceNode {
37 pub fn new(id: NodeId, element: &Element, state: &serde_json::Value) -> Self {
38 let props = resolve_props(&element.props, state);
39
40 Self {
41 id,
42 element_type: element.element_type.clone(),
43 props,
44 raw_props: element.props.clone(),
45 element_template: None,
46 key: element.key.clone(),
47 parent: None,
48 children: Vec::new(),
49 }
50 }
51
52 pub fn update_props(&mut self, state: &serde_json::Value) {
54 self.props = resolve_props(&self.raw_props, state);
55 }
56}
57
58pub struct InstanceTree {
60 nodes: SlotMap<NodeId, InstanceNode>,
62
63 root: Option<NodeId>,
65}
66
67impl InstanceTree {
68 pub fn new() -> Self {
69 Self {
70 nodes: SlotMap::with_key(),
71 root: None,
72 }
73 }
74
75 pub fn clear(&mut self) {
77 self.nodes.clear();
78 self.root = None;
79 }
80
81 pub fn create_node(&mut self, element: &Element, state: &serde_json::Value) -> NodeId {
83 let id = self.nodes.insert_with_key(|id| InstanceNode::new(id, element, state));
84 id
85 }
86
87 pub fn get(&self, id: NodeId) -> Option<&InstanceNode> {
89 self.nodes.get(id)
90 }
91
92 pub fn get_mut(&mut self, id: NodeId) -> Option<&mut InstanceNode> {
94 self.nodes.get_mut(id)
95 }
96
97 pub fn remove(&mut self, id: NodeId) -> Option<InstanceNode> {
99 if let Some(node) = self.nodes.get(id) {
100 let children = node.children.clone();
101 for child_id in children {
103 self.remove(child_id);
104 }
105 }
106 self.nodes.remove(id)
107 }
108
109 pub fn set_root(&mut self, id: NodeId) {
111 self.root = Some(id);
112 }
113
114 pub fn root(&self) -> Option<NodeId> {
116 self.root
117 }
118
119 pub fn add_child(&mut self, parent_id: NodeId, child_id: NodeId, before: Option<NodeId>) {
121 if let Some(parent) = self.nodes.get_mut(parent_id) {
122 if let Some(before_id) = before {
123 if let Some(pos) = parent.children.iter().position(|&id| id == before_id) {
124 parent.children.insert(pos, child_id);
125 } else {
126 parent.children.push(child_id);
127 }
128 } else {
129 parent.children.push(child_id);
130 }
131 }
132
133 if let Some(child) = self.nodes.get_mut(child_id) {
134 child.parent = Some(parent_id);
135 }
136 }
137
138 pub fn remove_child(&mut self, parent_id: NodeId, child_id: NodeId) {
140 if let Some(parent) = self.nodes.get_mut(parent_id) {
141 parent.children.retain(|&id| id != child_id);
142 }
143
144 if let Some(child) = self.nodes.get_mut(child_id) {
145 child.parent = None;
146 }
147 }
148
149 pub fn update_nodes(&mut self, node_ids: &indexmap::IndexSet<NodeId>, state: &serde_json::Value) {
151 for &node_id in node_ids {
152 if let Some(node) = self.nodes.get_mut(node_id) {
153 node.update_props(state);
154 }
155 }
156 }
157
158 pub fn iter(&self) -> impl Iterator<Item = (NodeId, &InstanceNode)> {
160 self.nodes.iter()
161 }
162}
163
164impl Default for InstanceTree {
165 fn default() -> Self {
166 Self::new()
167 }
168}
169
170fn resolve_props(props: &IndexMap<String, Value>, state: &serde_json::Value) -> IndexMap<String, serde_json::Value> {
172 let mut resolved = IndexMap::new();
173
174 for (key, value) in props {
175 let resolved_value = match value {
176 Value::Static(v) => v.clone(),
177 Value::Binding(binding) => {
178 evaluate_binding(binding, state).unwrap_or(serde_json::Value::Null)
180 }
181 Value::TemplateString { template, bindings } => {
182 let mut result = template.clone();
184 for binding in bindings {
185 let binding_placeholder = format!("${{state.{}}}", binding.full_path());
186 let value = evaluate_binding(binding, state)
187 .map(|v| match v {
188 serde_json::Value::String(s) => s,
189 other => other.to_string(),
190 })
191 .unwrap_or_default();
192 result = result.replace(&binding_placeholder, &value);
193 }
194 serde_json::Value::String(result)
195 }
196 Value::Action(action) => {
197 serde_json::Value::String(format!("@{}", action))
199 }
200 };
201 resolved.insert(key.clone(), resolved_value);
202 }
203
204 resolved
205}
206
207fn evaluate_binding(binding: &crate::reactive::Binding, state: &serde_json::Value) -> Option<serde_json::Value> {
209 let mut current = state;
210
211 for segment in &binding.path {
212 current = current.get(segment)?;
213 }
214
215 Some(current.clone())
216}
217
218#[cfg(test)]
219mod tests {
220 use super::*;
221 use serde_json::json;
222
223 #[test]
224 fn test_evaluate_binding() {
225 use crate::reactive::Binding;
226
227 let state = json!({
228 "user": {
229 "name": "Alice",
230 "age": 30
231 }
232 });
233
234 let name_binding = Binding::new(vec!["user".to_string(), "name".to_string()]);
235 let age_binding = Binding::new(vec!["user".to_string(), "age".to_string()]);
236 let email_binding = Binding::new(vec!["user".to_string(), "email".to_string()]);
237
238 assert_eq!(
239 evaluate_binding(&name_binding, &state),
240 Some(json!("Alice"))
241 );
242 assert_eq!(
243 evaluate_binding(&age_binding, &state),
244 Some(json!(30))
245 );
246 assert_eq!(evaluate_binding(&email_binding, &state), None);
247 }
248}