// One interpreter for addressing steps over the PathNode primitives, so step
// semantics (especially SeqAccessStep's live key resolution) are identical
// for every trait implementation.
///|
/// Resolve a field-referenced key against a node NOW: read key_field, ask the
/// scalar for its key form.
fn live_key(node : &PathNode, key_field : String) -> PathKey? {
match node.field(key_field) {
Some(k) => k.as_key()
None => None
}
}
///|
pub fn step_get(node : &PathNode, step : Step) -> &PathNode? {
match step {
FieldStep(name) => node.field(name)
SeqStep(field~, key~) | EachRenderItStep(field~, key~) =>
match node.field(field) {
Some(seq) => seq.item(key)
None => None
}
SeqAccessStep(seq_field~, key_field~) =>
match live_key(node, key_field) {
Some(key) =>
match node.field(seq_field) {
Some(seq) => seq.item(key)
None => None
}
None => None
}
BindStep(..) | ScopeBindStep(..) | EachBindStep(..) => Some(node) // frame-only: addresses nothing
}
}
///|
/// Rebuild one seq level: new seq with `key` replaced, then a new node with
/// the seq field replaced (both COW per the trait contract).
fn put_seq(
node : &PathNode,
field : String,
key : PathKey,
child : &PathNode,
) -> &PathNode? {
match node.field(field) {
Some(seq) =>
match seq.with_item(key, child) {
Some(new_seq) => node.with_field(field, new_seq)
None => None
}
None => None
}
}
///|
pub fn step_put(node : &PathNode, step : Step, child : &PathNode) -> &PathNode? {
match step {
FieldStep(name) => node.with_field(name, child)
SeqStep(field~, key~) | EachRenderItStep(field~, key~) =>
put_seq(node, field, key, child)
SeqAccessStep(seq_field~, key_field~) =>
match live_key(node, key_field) {
Some(key) => put_seq(node, seq_field, key, child)
None => None
}
BindStep(..) | ScopeBindStep(..) | EachBindStep(..) => Some(child) // frame-only: the new value passes through
}
}
///|
/// Freeze a field-resolved key against `node` as it is now (JS Step.pinKey):
/// SeqAccessStep becomes a literal-key SeqStep; every other step (and an
/// unresolvable key) pins to itself.
fn step_pin(step : Step, node : &PathNode) -> Step {
match step {
SeqAccessStep(seq_field~, key_field~) =>
match live_key(node, key_field) {
Some(key) => SeqStep(field=seq_field, key~)
None => step
}
_ => step
}
}
///|
/// The compact/dispatch projection of one step (JS toAbstractPathStep):
/// EachRenderItStep abstracts to a plain SeqStep; None drops the step — no
/// current variant is dropped, the hook exists for future frame-only steps.
fn step_to_abstract(step : Step) -> Step? {
match step {
EachRenderItStep(field~, key~) => Some(SeqStep(field~, key~))
BindStep(..) | ScopeBindStep(..) | EachBindStep(..) => None // frame-only steps are dropped from the dispatch path
_ => Some(step)
}
}
///|
/// Generic {field, key?} descriptor (JS Step.toKey). SeqAccessStep reports
/// its seq field with no key — the key is live and unknown without a value;
/// dropping the step instead would shift the indices of later keys.
fn step_to_key(step : Step) -> StepKey? {
match step {
FieldStep(name) => Some({ field: name, key: None })
SeqStep(field~, key~) | EachRenderItStep(field~, key~) =>
Some({ field, key: Some(key) })
SeqAccessStep(seq_field~, key_field=_) =>
Some({ field: seq_field, key: None })
BindStep(..) | ScopeBindStep(..) | EachBindStep(..) => None // frame-only: no field to report
}
}
///|
/// Key a step with the si/sk an `§Each§` meta carried (JS Step.withIndex /
/// withKey, used by event reconstruction's applyKey): a plain field access
/// under a keyed meta addresses the keyed item. Steps that carry their own
/// key (or no field) are returned unchanged.
pub fn step_with_key(step : Step, key : PathKey) -> Step {
match step {
FieldStep(f) => SeqStep(field=f, key~)
_ => step
}
}