1use crate::ir::{Element, NodeId, Value, IRNode, Props};
2use indexmap::IndexMap;
3use slotmap::SlotMap;
4use std::sync::Arc;
5
6#[derive(Debug, Clone)]
8pub enum ControlFlowKind {
9 ForEach {
11 item_name: String,
12 key_path: Option<String>,
13 },
14 Conditional,
16}
17
18#[derive(Debug, Clone)]
23pub struct InstanceNode {
24 pub id: NodeId,
26
27 pub element_type: String,
29
30 pub props: IndexMap<String, serde_json::Value>,
32
33 pub raw_props: Props,
35
36 pub element_template: Option<Arc<Element>>,
40
41 pub ir_node_template: Option<Arc<IRNode>>,
45
46 pub control_flow: Option<ControlFlowKind>,
48
49 pub key: Option<String>,
53
54 pub parent: Option<NodeId>,
56
57 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 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 pub fn update_props(&mut self, state: &serde_json::Value) {
104 self.props = resolve_props(&self.raw_props, state);
105 }
106
107 pub fn is_foreach(&self) -> bool {
109 matches!(self.control_flow, Some(ControlFlowKind::ForEach { .. }))
110 }
111
112 pub fn is_conditional(&self) -> bool {
114 matches!(self.control_flow, Some(ControlFlowKind::Conditional))
115 }
116}
117
118pub struct InstanceTree {
120 nodes: SlotMap<NodeId, InstanceNode>,
122
123 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 pub fn clear(&mut self) {
137 self.nodes.clear();
138 self.root = None;
139 }
140
141 pub fn create_node(&mut self, element: &Element, state: &serde_json::Value) -> NodeId {
143 let id = self.nodes.insert_with_key(|id| InstanceNode::new(id, element, state));
144 id
145 }
146
147 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(id, element_type, props, raw_props, control_flow, ir_node_template)
158 })
159 }
160
161 pub fn get(&self, id: NodeId) -> Option<&InstanceNode> {
163 self.nodes.get(id)
164 }
165
166 pub fn get_mut(&mut self, id: NodeId) -> Option<&mut InstanceNode> {
168 self.nodes.get_mut(id)
169 }
170
171 pub fn remove(&mut self, id: NodeId) -> Option<InstanceNode> {
173 if let Some(node) = self.nodes.get(id) {
174 let children = node.children.clone();
175 for child_id in children {
177 self.remove(child_id);
178 }
179 }
180 self.nodes.remove(id)
181 }
182
183 pub fn set_root(&mut self, id: NodeId) {
185 self.root = Some(id);
186 }
187
188 pub fn root(&self) -> Option<NodeId> {
190 self.root
191 }
192
193 pub fn add_child(&mut self, parent_id: NodeId, child_id: NodeId, before: Option<NodeId>) {
195 if let Some(parent) = self.nodes.get_mut(parent_id) {
196 if let Some(before_id) = before {
197 if let Some(pos) = parent.children.iter().position(|&id| id == before_id) {
198 parent.children.insert(pos, child_id);
199 } else {
200 parent.children.push_back(child_id);
201 }
202 } else {
203 parent.children.push_back(child_id);
204 }
205 }
206
207 if let Some(child) = self.nodes.get_mut(child_id) {
208 child.parent = Some(parent_id);
209 }
210 }
211
212 pub fn remove_child(&mut self, parent_id: NodeId, child_id: NodeId) {
214 if let Some(parent) = self.nodes.get_mut(parent_id) {
215 parent.children = parent.children.iter().filter(|&&id| id != child_id).copied().collect();
216 }
217
218 if let Some(child) = self.nodes.get_mut(child_id) {
219 child.parent = None;
220 }
221 }
222
223 pub fn update_nodes(&mut self, node_ids: &indexmap::IndexSet<NodeId>, state: &serde_json::Value) {
225 for &node_id in node_ids {
226 if let Some(node) = self.nodes.get_mut(node_id) {
227 node.update_props(state);
228 }
229 }
230 }
231
232 pub fn iter(&self) -> impl Iterator<Item = (NodeId, &InstanceNode)> {
234 self.nodes.iter()
235 }
236}
237
238impl Default for InstanceTree {
239 fn default() -> Self {
240 Self::new()
241 }
242}
243
244fn resolve_props(props: &IndexMap<String, Value>, state: &serde_json::Value) -> IndexMap<String, serde_json::Value> {
246 resolve_props_with_item(props, state, None)
247}
248
249fn resolve_props_with_item(
251 props: &IndexMap<String, Value>,
252 state: &serde_json::Value,
253 item: Option<&serde_json::Value>,
254) -> IndexMap<String, serde_json::Value> {
255 let mut resolved = IndexMap::new();
256
257 for (key, value) in props {
258 let resolved_value = match value {
259 Value::Static(v) => v.clone(),
260 Value::Binding(binding) => {
261 if binding.is_item() {
262 if let Some(item_value) = item {
264 evaluate_item_binding(binding, item_value).unwrap_or(serde_json::Value::Null)
265 } else {
266 serde_json::Value::Null
267 }
268 } else {
269 evaluate_binding(binding, state).unwrap_or(serde_json::Value::Null)
271 }
272 }
273 Value::TemplateString { template, .. } => {
274 match crate::reactive::evaluate_template_string(template, state, item) {
277 Ok(result) => serde_json::Value::String(result),
278 Err(_) => serde_json::Value::String(template.clone()),
279 }
280 }
281 Value::Action(action) => {
282 serde_json::Value::String(format!("@{}", action))
284 }
285 };
286 resolved.insert(key.clone(), resolved_value);
287 }
288
289 resolved
290}
291
292fn evaluate_item_binding(binding: &crate::reactive::Binding, item: &serde_json::Value) -> Option<serde_json::Value> {
294 if binding.path.is_empty() {
295 return Some(item.clone());
297 }
298
299 let mut current = item;
300 for segment in &binding.path {
301 current = current.get(segment)?;
302 }
303 Some(current.clone())
304}
305
306fn evaluate_binding(binding: &crate::reactive::Binding, state: &serde_json::Value) -> Option<serde_json::Value> {
308 let mut current = state;
309
310 for segment in &binding.path {
311 current = current.get(segment)?;
312 }
313
314 Some(current.clone())
315}
316
317#[cfg(test)]
318mod tests {
319 use super::*;
320 use serde_json::json;
321
322 #[test]
323 fn test_evaluate_binding() {
324 use crate::reactive::Binding;
325
326 let state = json!({
327 "user": {
328 "name": "Alice",
329 "age": 30
330 }
331 });
332
333 let name_binding = Binding::state(vec!["user".to_string(), "name".to_string()]);
334 let age_binding = Binding::state(vec!["user".to_string(), "age".to_string()]);
335 let email_binding = Binding::state(vec!["user".to_string(), "email".to_string()]);
336
337 assert_eq!(
338 evaluate_binding(&name_binding, &state),
339 Some(json!("Alice"))
340 );
341 assert_eq!(
342 evaluate_binding(&age_binding, &state),
343 Some(json!(30))
344 );
345 assert_eq!(evaluate_binding(&email_binding, &state), None);
346 }
347}