Phase 3.1-3.3: ReactiveTracker with ElementId assignment during parsing, dependency scanning, variable change notification in app.rs

This commit is contained in:
Glint Dev
2026-07-21 20:38:41 +03:00
parent a46c9bc9e7
commit d8d3f57a21
4 changed files with 249 additions and 20 deletions

View File

@@ -1,5 +1,6 @@
pub mod opcodes;
pub mod reactive;
pub mod reader;
pub mod rhei;
pub mod style;
@@ -9,6 +10,7 @@ use std::borrow::Cow;
pub use rhei::RheiContext;
use style::StyleSheet as SS;
pub use types::{ComponentDef, Document, Element, Interner, InterpError};
pub use reactive::{ElementId, ReactiveTracker};
use compact_str::CompactString;
use opcodes::*;
@@ -31,6 +33,7 @@ impl Interpreter {
let mut components = HashMap::new();
let mut rhei_scripts: Vec<String> = Vec::new();
let mut stylesheet = SS::new();
let mut tracker = ReactiveTracker::new();
let roots = Self::parse_block_elements(
&mut r,
@@ -38,13 +41,14 @@ impl Interpreter {
&mut components,
&mut rhei_scripts,
&mut stylesheet,
&mut tracker,
true,
)?;
//let rhei_ctx = RheiContext::new(&rhei_scripts);
//rhei_ctx.initialize(&mut variables);
Ok(Document { roots, components, variables, rhei_scripts, stylesheet, interner: Interner::new() })
Ok(Document { roots, components, variables, rhei_scripts, stylesheet, interner: Interner::new(), tracker })
}
fn parse_block_elements<'a>(
@@ -53,6 +57,7 @@ impl Interpreter {
components: &mut HashMap<String, ComponentDef<'a>>,
rhei_scripts: &mut Vec<String>,
stylesheet: &mut SS,
tracker: &mut ReactiveTracker,
is_root: bool,
) -> Result<Vec<Element<'a>>, InterpError> {
let mut roots: Vec<Element> = Vec::new();
@@ -63,8 +68,12 @@ impl Interpreter {
if !is_root && op == OP_END_BLOCK { break; }
match op {
OP_ELEM_PUSH => stack.push(Element::new(r.read_str_ref()?)),
OP_ELEM_PUSH => {
let elem_id = tracker.alloc_id();
let mut el = Element::new(r.read_str_ref()?);
el.element_id = elem_id;
stack.push(el);
}
OP_ELEM_POP => {
let finished = stack.pop().ok_or(InterpError::UnexpectedPop)?;
Self::attach(&mut stack, &mut roots, finished);
@@ -96,10 +105,12 @@ impl Interpreter {
val.push_str(RHEI_PREFIX);
val.push_str(expr_ref);
if let Some(el) = stack.last_mut() {
tracker.scan_value(el.element_id, &val);
el.push_prop("text".to_string(), val);
}
} else if let Some(value) = r.read_value_as_string(vop)? {
if let Some(el) = stack.last_mut() {
tracker.scan_value(el.element_id, &value);
el.push_prop("text".to_string(), value);
}
}
@@ -108,7 +119,10 @@ impl Interpreter {
OP_PROP_STR => {
let key = r.read_str_ref()?;
let val = r.read_str_ref()?;
if let Some(el) = stack.last_mut() { el.push_prop(key, val); }
if let Some(el) = stack.last_mut() {
tracker.scan_value(el.element_id, val);
el.push_prop(key, val);
}
}
OP_PROP_VAR => {
let key = r.read_str_ref()?;
@@ -116,22 +130,31 @@ impl Interpreter {
let mut val = String::with_capacity(val_ref.len() + 1);
val.push('$');
val.push_str(val_ref);
if let Some(el) = stack.last_mut() { el.push_prop(key, val); }
if let Some(el) = stack.last_mut() {
tracker.scan_value(el.element_id, &val);
el.push_prop(key, val);
}
}
OP_PROP_INT => {
let key = r.read_str_ref()?;
let val = r.read_i64()?.to_string();
if let Some(el) = stack.last_mut() { el.push_prop(key, val); }
if let Some(el) = stack.last_mut() {
el.push_prop(key, val);
}
}
OP_PROP_FLOAT => {
let key = r.read_str_ref()?;
let val = r.read_f64()?.to_string();
if let Some(el) = stack.last_mut() { el.push_prop(key, val); }
if let Some(el) = stack.last_mut() {
el.push_prop(key, val);
}
}
OP_PROP_BOOL => {
let key = r.read_str_ref()?;
let val = (r.read_byte()? != 0).to_string();
if let Some(el) = stack.last_mut() { el.push_prop(key, val); }
if let Some(el) = stack.last_mut() {
el.push_prop(key, val);
}
}
OP_PROP_RHEI => {
let key = r.read_str_ref()?;
@@ -140,13 +163,16 @@ impl Interpreter {
val.push_str(RHEI_PREFIX);
val.push_str(expr_ref);
if let Some(el) = stack.last_mut() {
tracker.scan_value(el.element_id, &val);
el.push_prop(key, val);
}
}
OP_PROP_UNIT | OP_PROP_CALL | OP_PROP_IDENT | OP_PROP_FSPATH | OP_PROP_COLOR => {
let key = r.read_str_ref()?;
if let Some(val) = r.read_value_as_string(op)? {
if let Some(el) = stack.last_mut() { el.push_prop(key, val); }
if let Some(el) = stack.last_mut() {
el.push_prop(key, val);
}
}
}
@@ -156,10 +182,10 @@ impl Interpreter {
rhei_scripts.push(script);
} else {
let mut text_el = Element::new("#text");
text_el.push_prop(
"text".to_string(),
format!("{RHEI_PREFIX}{script}"),
);
text_el.element_id = tracker.alloc_id();
let full_val = format!("{RHEI_PREFIX}{script}");
tracker.scan_value(text_el.element_id, &full_val);
text_el.push_prop("text".to_string(), full_val);
Self::attach(&mut stack, &mut roots, text_el);
}
}
@@ -171,7 +197,7 @@ impl Interpreter {
.map(|_| Ok((r.read_string()?, r.read_string()?)))
.collect::<Result<Vec<_>, InterpError>>()?;
let children = Self::parse_block_elements(
r, variables, components, rhei_scripts, stylesheet, false,
r, variables, components, rhei_scripts, stylesheet, tracker, false,
)?;
components.insert(name.clone(), ComponentDef { name, params, children });
}
@@ -186,23 +212,26 @@ impl Interpreter {
};
let true_children = Self::parse_block_elements(
r, variables, components, rhei_scripts, stylesheet, false,
r, variables, components, rhei_scripts, stylesheet, tracker, false,
)?;
let has_else = r.read_byte()? == 1;
let false_children = if has_else {
Self::parse_block_elements(
r, variables, components, rhei_scripts, stylesheet, false,
r, variables, components, rhei_scripts, stylesheet, tracker, false,
)?
} else {
Vec::new()
};
let mut if_el = Element::new("@if");
if_el.element_id = tracker.alloc_id();
tracker.scan_value(if_el.element_id, &cond_val);
if_el.push_prop("condition", cond_val);
if_el.children = true_children;
if !false_children.is_empty() {
let mut else_el = Element::new("@else");
else_el.element_id = tracker.alloc_id();
else_el.children = false_children;
if_el.children.push(else_el);
}
@@ -221,10 +250,12 @@ impl Interpreter {
};
let block_children = Self::parse_block_elements(
r, variables, components, rhei_scripts, stylesheet, false,
r, variables, components, rhei_scripts, stylesheet, tracker, false,
)?;
let mut each_el = Element::new("@each");
each_el.element_id = tracker.alloc_id();
tracker.scan_value(each_el.element_id, &source_val);
each_el.push_prop("var_name".to_string(), var_name);
each_el.push_prop("source".to_string(), source_val);
each_el.children = block_children;