use super::resolve::{resolve_props, resolve_props_full};
use crate::ir::{Element, IRNode, NodeId, Props};
use indexmap::IndexMap;
use slotmap::SlotMap;
use std::sync::Arc;
type DataSources = indexmap::IndexMap<String, serde_json::Value>;
pub type ResolvedProps = Arc<IndexMap<String, serde_json::Value>>;
pub const DEFAULT_ROUTER_CACHE_SIZE: usize = 10;
#[derive(Debug, Clone)]
pub enum ControlFlowKind {
ForEach {
item_name: String,
key_path: Option<String>,
},
Conditional,
Router {
cache: IndexMap<String, Vec<NodeId>>,
current_route_key: Option<String>,
max_cache_size: usize,
},
}
#[derive(Debug, Clone)]
pub struct InstanceNode {
pub id: NodeId,
pub element_type: String,
pub props: ResolvedProps,
pub raw_props: Props,
pub element_template: Option<Arc<Element>>,
pub ir_node_template: Option<Arc<IRNode>>,
pub control_flow: Option<ControlFlowKind>,
pub key: Option<String>,
pub parent: Option<NodeId>,
pub children: im::Vector<NodeId>,
pub module_scope: Option<String>,
}
impl InstanceNode {
pub fn new(id: NodeId, element: &Element, state: &serde_json::Value) -> Self {
Self::new_full(id, element, state, None)
}
pub fn new_full(
id: NodeId,
element: &Element,
state: &serde_json::Value,
data_sources: Option<&DataSources>,
) -> Self {
let props = resolve_props_full(&element.props, state, None, data_sources);
Self {
id,
element_type: element.element_type.clone(),
props,
raw_props: element.props.clone(),
element_template: None,
ir_node_template: None,
control_flow: None,
key: element.key.clone(),
parent: None,
children: im::Vector::new(),
module_scope: element.module_scope.clone(),
}
}
pub fn new_control_flow(
id: NodeId,
element_type: &str,
props: ResolvedProps,
raw_props: Props,
control_flow: ControlFlowKind,
ir_node_template: IRNode,
) -> Self {
Self {
id,
element_type: element_type.to_string(),
props,
raw_props,
element_template: None,
ir_node_template: Some(Arc::new(ir_node_template)),
control_flow: Some(control_flow),
key: None,
parent: None,
children: im::Vector::new(),
module_scope: None,
}
}
pub fn update_props(&mut self, state: &serde_json::Value) {
self.props = resolve_props(&self.raw_props, state);
}
pub fn update_props_with_data_sources(
&mut self,
state: &serde_json::Value,
data_sources: Option<&IndexMap<String, serde_json::Value>>,
) {
self.props = resolve_props_full(&self.raw_props, state, None, data_sources);
}
pub fn is_foreach(&self) -> bool {
matches!(self.control_flow, Some(ControlFlowKind::ForEach { .. }))
}
pub fn is_conditional(&self) -> bool {
matches!(self.control_flow, Some(ControlFlowKind::Conditional))
}
pub fn is_router(&self) -> bool {
matches!(self.control_flow, Some(ControlFlowKind::Router { .. }))
}
}
pub struct InstanceTree {
nodes: SlotMap<NodeId, InstanceNode>,
root: Option<NodeId>,
}
impl InstanceTree {
pub fn new() -> Self {
Self {
nodes: SlotMap::with_key(),
root: None,
}
}
pub fn clear(&mut self) {
self.nodes.clear();
self.root = None;
}
pub fn create_node(&mut self, element: &Element, state: &serde_json::Value) -> NodeId {
self.nodes
.insert_with_key(|id| InstanceNode::new(id, element, state))
}
pub fn create_node_full(
&mut self,
element: &Element,
state: &serde_json::Value,
data_sources: Option<&DataSources>,
) -> NodeId {
self.nodes
.insert_with_key(|id| InstanceNode::new_full(id, element, state, data_sources))
}
pub fn create_control_flow_node(
&mut self,
element_type: &str,
props: ResolvedProps,
raw_props: Props,
control_flow: ControlFlowKind,
ir_node_template: IRNode,
) -> NodeId {
self.nodes.insert_with_key(|id| {
InstanceNode::new_control_flow(
id,
element_type,
props,
raw_props,
control_flow,
ir_node_template,
)
})
}
pub fn get(&self, id: NodeId) -> Option<&InstanceNode> {
self.nodes.get(id)
}
pub fn get_mut(&mut self, id: NodeId) -> Option<&mut InstanceNode> {
self.nodes.get_mut(id)
}
pub fn remove(&mut self, id: NodeId) -> Option<InstanceNode> {
if let Some(node) = self.nodes.get(id) {
let children = node.children.clone();
for child_id in children {
self.remove(child_id);
}
}
self.nodes.remove(id)
}
pub fn set_root(&mut self, id: NodeId) {
self.root = Some(id);
}
pub fn root(&self) -> Option<NodeId> {
self.root
}
pub fn add_child(&mut self, parent_id: NodeId, child_id: NodeId, before: Option<NodeId>) {
if let Some(parent) = self.nodes.get_mut(parent_id) {
if let Some(before_id) = before {
if let Some(pos) = parent.children.iter().position(|&id| id == before_id) {
parent.children.insert(pos, child_id);
} else {
parent.children.push_back(child_id);
}
} else {
parent.children.push_back(child_id);
}
}
if let Some(child) = self.nodes.get_mut(child_id) {
child.parent = Some(parent_id);
}
}
pub fn remove_child(&mut self, parent_id: NodeId, child_id: NodeId) {
if let Some(parent) = self.nodes.get_mut(parent_id) {
parent.children = parent
.children
.iter()
.filter(|&&id| id != child_id)
.copied()
.collect();
}
if let Some(child) = self.nodes.get_mut(child_id) {
child.parent = None;
}
}
pub fn update_nodes(
&mut self,
node_ids: &indexmap::IndexSet<NodeId>,
state: &serde_json::Value,
) {
for &node_id in node_ids {
if let Some(node) = self.nodes.get_mut(node_id) {
node.update_props(state);
}
}
}
pub fn len(&self) -> usize {
self.nodes.len()
}
pub fn is_empty(&self) -> bool {
self.nodes.is_empty()
}
pub fn iter(&self) -> impl Iterator<Item = (NodeId, &InstanceNode)> {
self.nodes.iter()
}
}
impl Default for InstanceTree {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use crate::reconcile::resolve::evaluate_binding;
use serde_json::json;
#[test]
fn test_evaluate_binding() {
use crate::reactive::Binding;
let state = json!({
"user": {
"name": "Alice",
"age": 30
}
});
let name_binding = Binding::state(vec!["user".to_string(), "name".to_string()]);
let age_binding = Binding::state(vec!["user".to_string(), "age".to_string()]);
let email_binding = Binding::state(vec!["user".to_string(), "email".to_string()]);
assert_eq!(
evaluate_binding(&name_binding, &state),
Some(json!("Alice"))
);
assert_eq!(evaluate_binding(&age_binding, &state), Some(json!(30)));
assert_eq!(evaluate_binding(&email_binding, &state), None);
}
}