pub struct Sheet { /* private fields */ }Expand description
Owns a complete property model constraint graph.
Create cells with Sheet::add_cell, define multi-way constraints with
Sheet::add_relationship, write input values with Sheet::write,
then call Sheet::propagate to update derived cells. Propagation automatically
reuses eligible prepared plans or plans again when necessary.
§Example
use adam_rs::{Sheet, Method};
let mut sheet = Sheet::new();
let a = sheet.add_cell(0_i32);
let b = sheet.add_cell(0_i32);
sheet.add_relationship(vec![Method::from_fn_1_1(a, b, |x: &i32| Ok(*x * 2))]).unwrap();
sheet.write(a, 3_i32).unwrap();
assert_eq!(*sheet.read::<i32>(a).unwrap(), 3);Implementations§
Source§impl Sheet
impl Sheet
Sourcepub fn add_cell<T: Any + PartialEq + 'static>(&mut self, value: T) -> CellId
pub fn add_cell<T: Any + PartialEq + 'static>(&mut self, value: T) -> CellId
Registers a cell with an initial value and returns a stable handle.
The cell’s TypeId is fixed at creation time; subsequent write and
read calls that use a different type will return Error::TypeMismatch.
Each call increments the sheet’s internal strength counter and sets bit 63 of the result. This partitions the strength space: written/added cells always have higher strength than derived cells, ensuring stability across conditional branch switches.
- Complexity: Amortized O(1), plus O(A) when invalidation releases the final owners of prepared artifacts of total size A.
Sourcepub fn add_source<T: Any + PartialEq + 'static>(&mut self, value: T) -> CellId
pub fn add_source<T: Any + PartialEq + 'static>(&mut self, value: T) -> CellId
Registers a cell that can never be claimed as any method’s output — always a planner source, forever.
- Complexity: Amortized O(1), plus O(A) when invalidation releases the final owners of prepared artifacts of total size A.
Sourcepub fn cell_kind(&self, id: CellId) -> Option<CellKind>
pub fn cell_kind(&self, id: CellId) -> Option<CellKind>
Returns id’s fixed cell kind.
Returns None if id is not a live cell in this sheet.
Sourcepub fn add_relationship(
&mut self,
methods: Vec<Method>,
) -> Result<RelationshipId, Error>
pub fn add_relationship( &mut self, methods: Vec<Method>, ) -> Result<RelationshipId, Error>
Registers a relationship defined by a non-empty list of methods.
All methods are validated: their declared TypeIds must match the
registered cells, and each method must have at least one output. A method
with no inputs is explicitly allowed: it defines a fixed point (a constant,
independent of every other cell) rather than a derivation.
On success the RelationshipId is added to each adjacent cell’s adjacency list.
A cell that appears in both a method’s inputs and its outputs is a self-referencing cell and is explicitly allowed.
§Errors
Error::InvalidMethod—methodsis empty, or a method has no outputs.Error::MismatchedMethodCells— some method’sinputs ∪ outputsdiffers from another method’s in the same relationship.Error::InvalidMethodOutputs— a method’s ownoutputslist names a cell more than once, or two methods in the same relationship have identical or nestedoutputssets. Overlapping non-nested output sets are allowed.Error::InvalidId— aCellIdin any method is not found in this sheet.Error::TypeMismatch— a method’s declaredTypeIddoes not match the cell’s registeredTypeId.Error::InvalidCellKind— a method’s output cell isSource-kind.
Guard independence is checked by Sheet::validate and by Sheet::propagate,
not by this mutator.
- Complexity: O(m² × c) where m is the total number of methods and c is the maximum number of cells per method, due to pairwise output-set comparison, plus O(A) when invalidation releases the final owners of prepared artifacts of total size A.
Sourcepub fn add_conditional<T: Any + PartialEq + 'static>(
&mut self,
source: MatchExpr,
branches: Vec<(Vec<T>, Vec<RelationshipId>)>,
default: Vec<RelationshipId>,
) -> Result<ConditionalId, Error>
pub fn add_conditional<T: Any + PartialEq + 'static>( &mut self, source: MatchExpr, branches: Vec<(Vec<T>, Vec<RelationshipId>)>, default: Vec<RelationshipId>, ) -> Result<ConditionalId, Error>
Registers a conditional that activates relationships based on the value of a match
subject: either a single existing cell, or a MatchExpr computed from multiple
input cells.
Each element of branches is (keys, relationships): when the match subject’s
value equals any key in keys, the branch’s relationships are added to the active
set for propagate. Branches are evaluated in definition order; first match wins.
default holds relationships activated when no branch matches; pass an empty Vec
for no default.
§Errors
Error::InvalidId— the match subject references a cell not in this sheet.Error::TypeMismatch— (expression match subject only) an input cell’s registered type doesn’t match the expression’s declared type for that input.Error::InvalidConditional— the match subject’s output type does not matchT; a referenced relationship does not exist; a relationship already appears in another conditional branch; or a branch has no keys.
Guard independence is checked by Sheet::validate and by Sheet::propagate,
not by this mutator.
- Complexity: O(B·(K + R)) where B = branches, K = keys per branch, R = relationships per branch, plus O(A) when invalidation releases the final owners of prepared artifacts of total size A.
Sourcepub fn add_requirement(
&mut self,
cell: CellId,
requirement: Requirement,
) -> Result<RequirementId, Error>
pub fn add_requirement( &mut self, cell: CellId, requirement: Requirement, ) -> Result<RequirementId, Error>
Attaches a requirement to cell and returns its stable ID. requirement.inputs may be any cells in
the sheet, not only cell itself. For a Cell/Source kind cell, also
evaluates requirement immediately against current effective values — its
value is already authoritative. Skipped for an Out-kind cell: its value
isn’t authoritative until its writer next executes, so attachment always
succeeds structurally there, deferring to the first post-propagate()
diagnostic to report an initial violation if there is one.
§Errors
-
Error::InvalidId—cell, or one ofrequirement’s input cells, is not a live cell in this sheet. -
Error::TypeMismatch— an input’s declared type does not match its cell’s registered type. -
Error::InvalidRequirement— the input signature is invalid, or (Cell/Sourcekind only) evaluatingrequirementagainst the referenced cells’ current effective values returnsOk(false). -
Error::MethodFailed— (Cell/Sourcekind only) evaluatingrequirementagainst current values returnsErr. -
Complexity: O(k) where k is
requirement’s input count, plus predicate evaluation for aCell/Source, plus O(A) when invalidation releases the final owners of prepared artifacts of total size A.
§Examples
use adam_rs::{Requirement, Sheet};
let mut sheet = Sheet::new();
let cell = sheet.add_cell(1_i32);
let id = sheet.add_requirement(cell, Requirement::from_fn_1(cell, |x: &i32| Ok(*x > 0)))?;
assert_eq!(sheet.cell_requirements(cell), Some([id].as_slice()));Sourcepub fn add_out(
&mut self,
writer: Method,
requirements: Vec<Requirement>,
) -> Result<CellId, Error>
pub fn add_out( &mut self, writer: Method, requirements: Vec<Requirement>, ) -> Result<CellId, Error>
Registers writer as the sole producer of its one output cell, which becomes
an out cell: always derived by writer, never write()-able, but otherwise
an ordinary, freely-referenceable cell. requirements are attached to that
cell one at a time, in order, via Sheet::add_requirement.
§Errors
-
Error::InvalidOutput—writerdoes not have exactly one output cell. -
Error::InvalidCellKind— the writer’s output cell is alreadySourceorOutkind, or already claimed as some existing method’s output. -
Any error
Sheet::add_relationshiporSheet::add_requirementcan return. -
Postcondition: not atomic — if
add_requirementfails partway throughrequirements, the output cell is leftOut-kind,writeris already registered as its producer, and every requirement before the failing one is already attached; none of this is rolled back on error. -
Complexity: O(k + i + m²×c) where k is the number of requirements and i is their total input count, plus the cost of
add_relationshipforwriteralone (m = 1 method, c = cells in that method), plus O(A) when invalidation releases the final owners of prepared artifacts of total size A.
§Examples
use adam_rs::{Method, Requirement, Sheet};
let mut sheet = Sheet::new();
let input = sheet.add_cell(1_i32);
let output = sheet.add_cell(0_i32);
let id = sheet.add_out(
Method::from_fn_1_1(input, output, |x: &i32| Ok(*x)),
vec![Requirement::from_fn_1(output, |x: &i32| Ok(*x > 0))],
)?;
assert_eq!(id, output);
sheet.propagate()?;
assert!(sheet.cell_requirements_valid(output));Sourcepub fn add_filter(&mut self, cell: CellId, filter: Filter) -> Result<(), Error>
pub fn add_filter(&mut self, cell: CellId, filter: Filter) -> Result<(), Error>
Attaches filter to cell.
Never evaluates filter’s function — attaching a filter is not a fresh
external input, so it never changes cell’s current effective value. The next
full Sheet::propagate call conforms cell via the planner’s
PlanStep::FilterReclamp step; until then, read() reflects whatever cell
held before this call.
§Errors
Error::InvalidId—cell, or one offilter’s argument cells, is not a live cell in this sheet.Error::InvalidFilter—cellalready has a filter,filter’s own value type does not matchcell’s registered type, orfilter’s argument list namescellitself.Error::TypeMismatch— an argument cell’s registered type does not match the typefilterdeclared for it.
Guard independence is checked by Sheet::validate and by Sheet::propagate,
not by this mutator.
- Complexity: O(a) where a is the number of filter argument cells, plus O(A) when invalidation releases the final owners of prepared artifacts of total size A.
Sourcepub fn filter_args(&self, id: CellId) -> Option<&[CellId]>
pub fn filter_args(&self, id: CellId) -> Option<&[CellId]>
Returns the argument cells of id’s filter, in declaration order.
Returns None if id is not a live cell in this sheet, or has no filter.
Sourcepub fn filter_dependents(&self, id: CellId) -> &[CellId]
pub fn filter_dependents(&self, id: CellId) -> &[CellId]
Returns the live cells whose filter references id as one of its dynamic
arguments — the reverse of a filter’s own argument list (Sheet::filter_args).
- Postcondition: empty if no live cell’s filter references
id.
Sourcepub fn filter_kind(&self, id: CellId) -> Option<&FilterKind>
pub fn filter_kind(&self, id: CellId) -> Option<&FilterKind>
Returns the kind of validation/derivation id’s filter performs, if it has one.
Returns None if id is not a live cell in this sheet, or has no filter.
Sourcepub fn filter_range<T: Any + Clone>(&self, id: CellId) -> Option<(T, T)>
pub fn filter_range<T: Any + Clone>(&self, id: CellId) -> Option<(T, T)>
Returns id’s filter’s current (lo, hi) bounds, if it has a FilterKind::Range
filter.
Resolves the filter’s argument cells’ current effective values via the same path
Sheet::add_filter already uses, then calls the filter’s bounds function.
Returns None if id is not a live cell in this sheet, has no filter, its filter’s
kind isn’t FilterKind::Range, or the range expression fails to evaluate against
the filter’s current argument values (e.g. a fallible arithmetic op in a range
endpoint) — the same degraded-to-None outcome as any other reason no live bounds
are available right now.
- Complexity: O(a) where a is the number of the filter’s argument cells.
Sourcepub fn filter_violation(&self, id: CellId) -> Option<&FilterViolation>
pub fn filter_violation(&self, id: CellId) -> Option<&FilterViolation>
Returns the filter violation recorded for id as of the last full
propagate() call, if any.
- Postcondition:
Noneifidhas no filter,id’s filter’s last-checked value held, or no fullpropagate()has run sinceidwas last a plain external write.
Sourcepub fn filter_violated_cells(&self) -> impl Iterator<Item = CellId> + '_
pub fn filter_violated_cells(&self) -> impl Iterator<Item = CellId> + '_
Iterates cells whose filter is currently violated, as of the last full
propagate() call.
- Complexity: O(n) where n is the number of currently-violated filters.
Sourcepub fn filter_violation_cells(&self) -> HashSet<CellId>
pub fn filter_violation_cells(&self) -> HashSet<CellId>
Returns the set of root cells currently determining a violated filter’s own
value or any of its argument values, as of the last full propagate() call —
the same “which upstream cells caused this” query
requirement_contributing_cells/requirement_violation_cells already provide
for Requirement.
- Postcondition: empty if no filter is currently violated.
- Complexity: O(sum of
contributing_cellscost over every violated filter and its argument cells).
Sourcepub fn cell_requirements(&self, id: CellId) -> Option<&[RequirementId]>
pub fn cell_requirements(&self, id: CellId) -> Option<&[RequirementId]>
Returns the requirements attached to id, in attachment order.
Returns None if id is not a live cell in this sheet.
Sourcepub fn requirement_cell(&self, id: RequirementId) -> Option<CellId>
pub fn requirement_cell(&self, id: RequirementId) -> Option<CellId>
Returns the cell requirement id is attached to.
Returns None if id is not a live requirement in this sheet.
Sourcepub fn requirement_inputs(&self, id: RequirementId) -> Option<&[CellId]>
pub fn requirement_inputs(&self, id: RequirementId) -> Option<&[CellId]>
Returns the cells requirement id reads.
Returns None if id is not a live requirement in this sheet.
Sourcepub fn cell_requirements_valid(&self, id: CellId) -> bool
pub fn cell_requirements_valid(&self, id: CellId) -> bool
Returns true if every requirement on id held as of the last propagate()
call.
Returns false if no propagation has run yet. Also returns true for an
id that is not a live cell in this sheet, since no requirement can have
failed for a cell that doesn’t exist.
Sourcepub fn violated_requirements(
&self,
id: CellId,
) -> impl Iterator<Item = RequirementId> + '_
pub fn violated_requirements( &self, id: CellId, ) -> impl Iterator<Item = RequirementId> + '_
Iterates the requirements on id that evaluated to false as of the last
propagate() call.
- Postcondition: empty if
id’s requirements all held,idis not a live cell in this sheet, or no propagation has run yet.
Sourcepub fn contributing_cells(&self, id: CellId) -> HashSet<CellId>
pub fn contributing_cells(&self, id: CellId) -> HashSet<CellId>
Returns the set of root cells that could determine id’s value for some choice of
cell strengths, as of the last propagate() call.
A cell is a root candidate for current whenever some active relationship
adjacent to current has a method producing it — not just the one method the
current strengths happen to have selected: a different strength ordering could pick
a different method of that same relationship, making any of its other cells the
source instead. So every method (of every active relationship touching current)
with current among its outputs contributes its inputs to the walk, not only the
currently-selected one. Sheet::is_forced already answers “could current itself
be left as a free source under some strength” precisely, so current is added
directly whenever it is not forced. A self-referencing input (present in both a
method’s inputs and its outputs) is treated as one of its own roots directly, since
it is read at its pre-execution value rather than derived further.
Every visited cell is also checked against every conditional in the sheet: if any of that conditional’s branches (or its default) has a method whose outputs include the visited cell, the conditional’s match cell is added as a contributor too, and is itself traced recursively. This covers both an active producer that happens to belong to a conditional branch, and a cell with no active producer specifically because the branch that would define it isn’t the one currently selected (that absence is itself a fact controlled by the match cell, not an indication that the match cell is irrelevant).
-
Postcondition: returns
{id}if no propagation has run yet, or ifidis not forced and no active or conditional relationship could produce it. -
Complexity: O(N · (R·M + B)) where N is the number of cells reachable from
id(including conditional match cells), R is the number of relationships adjacent to a visited cell, M is the maximum number of methods per relationship, and B is the total number of branch/default relationships across all conditionals.
Sourcepub fn requirement_contributing_cells(
&self,
id: RequirementId,
) -> HashSet<CellId>
pub fn requirement_contributing_cells( &self, id: RequirementId, ) -> HashSet<CellId>
Returns the union of Sheet::contributing_cells over requirement id’s own
declared inputs.
Returns an empty set if id is not a live requirement in this sheet.
- Complexity: O(K·N) where K is the requirement’s input count and N is the size of each input’s contributing set.
Sourcepub fn requirement_relevant_cells(&self) -> HashSet<CellId>
pub fn requirement_relevant_cells(&self) -> HashSet<CellId>
Returns the union of contributing_cells over every cell with at least one
requirement — the set of cells currently determining at least one
requirement-checked value, as of the last propagate() call.
- Postcondition: empty if no cell in the sheet has any requirements.
- Complexity: O(sum of
contributing_cellscost over every cell with requirements).
Sourcepub fn requirement_violation_cells(&self) -> HashSet<CellId>
pub fn requirement_violation_cells(&self) -> HashSet<CellId>
Returns the union of requirement_contributing_cells over every requirement
that evaluated false as of the last propagate() call, across every cell
in the sheet.
- Postcondition: empty if no requirement anywhere in the sheet currently fails.
- Complexity: O(sum of
requirement_contributing_cellscost over every violated requirement).
Sourcepub fn write<T: Any + 'static>(
&mut self,
id: CellId,
value: T,
) -> Result<(), Error>
pub fn write<T: Any + 'static>( &mut self, id: CellId, value: T, ) -> Result<(), Error>
Writes a value to a cell, incrementing the cell’s write-recency strength.
Each successful write increments a global monotonic counter and assigns
the new value to cell.strength, so the most-recently-written cell always
has the highest strength.
- Postcondition: any pending derived override is cleared, so the written value is immediately visible via
read(). - Complexity: Expected O(1) validation and phase-membership work, plus O(A) when a claimed-cell write releases the final owners of prepared artifacts of total size A, excluding destruction of replaced values.
§Errors
Error::InvalidId—idis not a cell in this sheet.Error::TypeMismatch—Tdoes not match the cell’s registeredTypeId.Error::InvalidCellKind—idisOut-kind.
Sourcepub fn read<T: Any + 'static>(&self, id: CellId) -> Result<&T, Error>
pub fn read<T: Any + 'static>(&self, id: CellId) -> Result<&T, Error>
Returns a shared reference to the cell’s effective current value: its derived override if one exists, otherwise its source (last written) value.
§Errors
Error::InvalidId—idis not a cell in this sheet.Error::TypeMismatch—Tdoes not match the cell’s registeredTypeId.
Sourcepub fn source<T: Any + 'static>(&self, id: CellId) -> Result<&T, Error>
pub fn source<T: Any + 'static>(&self, id: CellId) -> Result<&T, Error>
Returns the raw source slot: the last value written via write()/add_cell,
ignoring any derived override produced by a self-referencing method or a
conditionally forced relationship. For an ordinary (unshadowed) derived cell,
propagate() writes straight into this same slot, so source() agrees with
read(); the two diverge only for cells currently shadowed.
§Errors
Error::InvalidId—idis not a cell in this sheet.Error::TypeMismatch—Tdoes not match the cell’s registeredTypeId.
§Examples
use adam_rs::Sheet;
let mut sheet = Sheet::new();
let a = sheet.add_cell(3_i32);
sheet.write(a, 8_i32).unwrap();
assert_eq!(*sheet.source::<i32>(a).unwrap(), 8);Sourcepub fn changed(&self) -> impl Iterator<Item = CellId> + '_
pub fn changed(&self) -> impl Iterator<Item = CellId> + '_
Iterates over the cells that were updated during the last propagate() call.
This includes cells written by selected methods and cells that reverted to their
source values because the relationship that had been shadowing them (self-referencing
or conditionally forced) is no longer producing them this round (Phase 5), even though
no method wrote to them this round. It does not attempt to compare old/new values for
equality. A propagate() call that returns Error::SeedCycle leaves this iterator
empty: propagation clears stale changed-state before staged seed validation and
aborts before committing any writes.
- Complexity: O(n) where n is the number of changed cells.
Sourcepub fn clear_changed(&mut self)
pub fn clear_changed(&mut self)
Clears the changed-cell set and resets each cell’s changed flag.
Call after processing the results of propagate().
- Complexity: O(n) where n is the number of changed cells.
Sourcepub fn validate(&mut self) -> Result<(), Error>
pub fn validate(&mut self) -> Result<(), Error>
Validates static guard independence for the current sheet structure.
A filter’s argument cells and a conditional’s match cells must not depend on any
cell governed by that filter or conditional in the static dependency graph. A
successful call records the current structure as validated; a failed call leaves
it unvalidated, so the same structure reports the dependency cycle again on the
next Sheet::validate or Sheet::propagate call.
§Errors
Error::DependencyCycle— a guard edge lies on a cycle in the static dependency graph.
§Examples
use adam_rs::{Filter, Method, Sheet};
let mut sheet = Sheet::new();
let limit = sheet.add_cell(10_i32);
let value = sheet.add_cell(5_i32);
let copy = sheet.add_cell(0_i32);
sheet
.add_relationship(vec![Method::from_fn_1_1(value, copy, |x: &i32| Ok(*x))])
.unwrap();
sheet
.add_filter(value, Filter::from_fn_1(limit, |v: &i32, hi: &i32| Ok((*v).min(*hi))))
.unwrap();
sheet.validate().unwrap();- Complexity: O(1) when no structural mutation has happened since the last successful validation; otherwise O(V + E), where V is the number of cells and E is the number of static method and guard edges.
Sourcepub fn cells(&self) -> impl Iterator<Item = CellId> + '_
pub fn cells(&self) -> impl Iterator<Item = CellId> + '_
Iterates all live cell IDs in the sheet.
- Complexity: O(n) where n is the number of cells.
Sourcepub fn relationships(&self) -> impl Iterator<Item = RelationshipId> + '_
pub fn relationships(&self) -> impl Iterator<Item = RelationshipId> + '_
Iterates all live relationship IDs in the sheet.
- Complexity: O(n) where n is the number of relationships.
Sourcepub fn cell_adj(&self, id: CellId) -> Option<&[RelationshipId]>
pub fn cell_adj(&self, id: CellId) -> Option<&[RelationshipId]>
Returns the relationships adjacent to id.
Returns None if id is not a live cell in this sheet.
- Complexity: O(1).
Sourcepub fn relationship_adj(&self, id: RelationshipId) -> Option<&[CellId]>
pub fn relationship_adj(&self, id: RelationshipId) -> Option<&[CellId]>
Returns the cells adjacent to id (union across all methods).
Returns None if id is not a live relationship in this sheet.
- Complexity: O(1).
Sourcepub fn propagate(&mut self) -> Result<(), Error>
pub fn propagate(&mut self) -> Result<(), Error>
Updates the sheet using eligible prepared plans or newly selected methods.
Validates static guard independence before mutating state, then clears the
changed-cell set from the previous propagate() call before staged planning.
After propagation, call Sheet::changed to inspect which cells were updated,
and Sheet::clear_changed when done.
Method, filter, conditional-expression, and requirement callbacks must be purely functional. There is no guarantee that any callback executes, or when or how often it executes; results may be reused or recomputed while preserving cell values and diagnostics. The phases below describe the current algorithm, not a callback invocation contract.
Retains at most one prepared unconditional plan and one prepared main plan, each with its own released sources and active relationships. Successful writes to released sources preserve that phase’s eligibility; writes to claimed cells (including self-referencing claimants) discard it. Filter reclamps do not claim released sources. Actual structural mutations discard both entries, including partial mutations before an error; rejected writes leave eligibility unchanged. Prepared plans retain structural seed recipes, not values from earlier calls.
Phase 0 — Staging: propagation starts from each cell’s source value, excluding every derived override from the previous round, but does not mutate live cells.
Phase 1 — Pre-plan: when conditionals exist, an eligible prepared unconditional plan is obtained or a new one is prepared. Its selected guard-prerequisite cone executes so derived guard values are current before branch evaluation. Unrelated filters remain deferred until the final active plan, after the relationships that produce their arguments.
Phase 2 — Conditional evaluation: each conditional’s match cell value is read and compared against branch keys; the active relationship set is built.
Phase 3 — General plan: an eligible prepared main plan is obtained for the active set, or the Adam algorithm selects and prepares a new one. Seed-cycle validation and method execution consume staged Phase 1 values. A callback already evaluated in Phase 1 is reused only when the final plan preserves its selected method, input producers, and output classification. A mismatched plan returns a conservative conflict rather than committing stale values. This boundary may reject a sheet even when another assignment could have avoided the mismatch; propagation reports the implicated sites instead of attempting that alternate assignment. A Phase 1 seed callback is likewise reused only when every input reads the same staged source version, recursive seed result, or accumulated value; otherwise propagation returns a conflict instead of reusing a stale seed.
Phase 4 — Commit and strength post-processing: after planning, seed validation, and staged method execution all succeed, staged writes are published atomically. Derived cells then receive low-order strengths in evaluation order, enforcing the stability invariant. A cell claimed self-referencingly keeps any live explicit strength instead, since its own written value is still the authority behind the result.
Phase 5 — Reversion change-tracking: a cell whose derived override existed before this round but wasn’t reclaimed by any method this round has effectively reverted to its source value (e.g. its forcing conditional went inactive); it is marked changed even though no method wrote to it this round.
Phase 6 — Requirement evaluation: every registered requirement is evaluated
against current cell values, rebuilding last_requirement_violations from
scratch, so Sheet::cell_requirements_valid and Sheet::violated_requirements
reflect this round.
If staged seed validation detects a non-self sibling dependency cycle, or compatibility
checking returns a prerequisite conflict, propagate() returns its error after validation
and changed-state clearing but before commit. Live cell values therefore remain untouched
and Sheet::changed stays empty for that failing call.
Requirement callback errors occur after commit: published values and changed-state remain visible. Any propagation error discards both phases’ reuse eligibility, but retains the last successful display snapshot. Static validation errors occur before changed-state clearing. Filter failures remain diagnostics rather than propagation errors, and diagnostic results use current values.
- Complexity: on a cache miss, prerequisite-cone indexing, traversal, and producer preparation take O(V + E) per selected plan, distinct from assignment matching. Cache hits omit this preparation, assignment matching, global strength sorting, and complete seed-elimination replay. Active-set comparison takes expected O(R) for R active relationships; a pending strength certificate takes O(C) for C cells. Current strength gates and local sibling sorts still run (O(S log S) comparisons for S siblings, with structural signature comparison costs). Fresh staged execution, seed evaluation, commit, and diagnostics retain their costs. Discarding final owners of prepared artifacts of total size A can take O(A).
§Errors
Error::DependencyCycle— a filter or conditional guard edge lies on a cycle in the static dependency graph.Error::Conflict— no valid method assignment exists, or a pre-executed guard prerequisite is incompatible with the final selected plan.Error::SeedCycle— sibling seed dependencies form a non-self cycle.Error::MethodFailed— a method’s function returned an error, a method produced the wrong number of outputs, or a requirement’s function returned an error.Error::TypeMismatch— a method output’s runtime type does not match the cell’s registered type.
Sourcepub fn selected_method(&self, rel: RelationshipId) -> Option<usize>
pub fn selected_method(&self, rel: RelationshipId) -> Option<usize>
Returns the index of the method selected for rel in the last successful propagation.
Returns None if no propagation has run yet, rel is not in the cached plan,
or rel was added after the last propagate() call.
Sourcepub fn method_inputs(
&self,
rel: RelationshipId,
idx: usize,
) -> Option<&[CellId]>
pub fn method_inputs( &self, rel: RelationshipId, idx: usize, ) -> Option<&[CellId]>
Returns the input cells of method idx in relationship rel.
Returns None if rel is not a live relationship or idx is out of bounds.
Sourcepub fn method_outputs(
&self,
rel: RelationshipId,
idx: usize,
) -> Option<&[CellId]>
pub fn method_outputs( &self, rel: RelationshipId, idx: usize, ) -> Option<&[CellId]>
Returns the output cells of method idx in relationship rel.
Returns None if rel is not a live relationship or idx is out of bounds.
Sourcepub fn is_source(&self, id: CellId) -> bool
pub fn is_source(&self, id: CellId) -> bool
Returns true if id was not written by any selected method in the last successful propagation.
Returns false if no propagation has succeeded yet. This display query does not
determine prepared-plan eligibility.
Cells absent from the completed plan, including newly added cells, return true.
- Complexity: Expected O(1).
Sourcepub fn is_forced(&self, id: CellId) -> bool
pub fn is_forced(&self, id: CellId) -> bool
Returns true if id can never be a source, as of the last successful
propagate() call.
Some active relationship’s method structure guarantees the cell is always
produced by a method, regardless of strength — writing to it has no lasting
effect once propagate() runs again. Useful for disabling input fields in a UI.
Returns false if no propagation has run yet.
Sourcepub fn forced_cells(&self) -> impl Iterator<Item = CellId> + '_
pub fn forced_cells(&self) -> impl Iterator<Item = CellId> + '_
Iterates cells that are forced (see Sheet::is_forced) as of the last
successful propagate() call.
- Complexity: O(n) where n is the number of forced cells.
Sourcepub fn is_relationship_forced(&self, id: RelationshipId) -> bool
pub fn is_relationship_forced(&self, id: RelationshipId) -> bool
Returns true if id had exactly one viable method as of the last successful
propagate() call — the planner has no alternative method to choose for this
relationship, regardless of cell strength.
Returns false if no propagation has run yet.
Sourcepub fn forced_relationships(&self) -> impl Iterator<Item = RelationshipId> + '_
pub fn forced_relationships(&self) -> impl Iterator<Item = RelationshipId> + '_
Iterates relationships that are forced (see Sheet::is_relationship_forced)
as of the last successful propagate() call.
- Complexity: O(n) where n is the number of forced relationships.
Sourcepub fn conditionals(&self) -> impl Iterator<Item = ConditionalId> + '_
pub fn conditionals(&self) -> impl Iterator<Item = ConditionalId> + '_
Iterates all live conditional IDs in the sheet.
- Complexity: O(n) where n is the number of conditionals.
Sourcepub fn out_cells(&self) -> impl Iterator<Item = CellId> + '_
pub fn out_cells(&self) -> impl Iterator<Item = CellId> + '_
Iterates all live Out-kind cells in the sheet.
- Complexity: O(n) where n is the number of cells.
Sourcepub fn conditional_match_cells(&self, id: ConditionalId) -> Option<&[CellId]>
pub fn conditional_match_cells(&self, id: ConditionalId) -> Option<&[CellId]>
Returns the match cells for conditional id: a single cell for a plain match
subject, or every input of a MatchExpr match subject.
Returns None if id is not a live conditional in this sheet.
Sourcepub fn conditional_branch_count(&self, id: ConditionalId) -> Option<usize>
pub fn conditional_branch_count(&self, id: ConditionalId) -> Option<usize>
Returns the number of named branches in conditional id.
Returns None if id is not a live conditional in this sheet.
Sourcepub fn conditional_branch_relationships(
&self,
id: ConditionalId,
branch: usize,
) -> Option<&[RelationshipId]>
pub fn conditional_branch_relationships( &self, id: ConditionalId, branch: usize, ) -> Option<&[RelationshipId]>
Returns the relationship IDs for branch branch of conditional id.
Returns None if id is not a live conditional, or branch is out of bounds.
Sourcepub fn conditional_default_relationships(
&self,
id: ConditionalId,
) -> Option<&[RelationshipId]>
pub fn conditional_default_relationships( &self, id: ConditionalId, ) -> Option<&[RelationshipId]>
Returns the default relationship IDs for conditional id.
These relationships are active when no named branch key matches the match cell.
Returns None if id is not a live conditional in this sheet.
Sourcepub fn conditional_active_branch(
&self,
id: ConditionalId,
) -> Result<Option<usize>, Error>
pub fn conditional_active_branch( &self, id: ConditionalId, ) -> Result<Option<usize>, Error>
Returns the index of the currently matching branch for conditional id.
Evaluates branch keys against the match subject’s current value in definition
order; returns the index of the first matching branch. Returns Ok(None) if no
branch key matches (the default branch is active) or if id is not a live
conditional.
§Errors
-
Error::MethodFailed—idis a live, expression-sourced conditional whose function returned an error. -
Complexity: O(B·K) where B = branches, K = keys per branch.