Skip to main content

Module sheet

Module sheet 

Source
Expand description

The Sheet owns and manages a property model constraint graph.

All cells and relationships are created through the sheet and are destroyed when the sheet is dropped.

Queries borrow declarations or describe a completed propagation; they do not propagate implicitly. This graph combines a conditional relationship, a fixed writer, a live source filter and a requirement.

use adam_rs::{CellKind, Filter, MatchExpr, Method, Requirement, Sheet};
use std::any::TypeId;
let mut sheet = Sheet::default();
let mode = sheet.add_source(true);
let limit = sheet.add_source(100_i32);
let a = sheet.add_cell(7_i32);
let b = sheet.add_cell(0_i32);
let out = sheet.add_cell(0_i32);
sheet.add_filter(a, Filter::range(TypeId::of::<i32>(), vec![limit],
    vec![TypeId::of::<i32>()],
    |v, args| Ok(Box::new((*v.downcast_ref::<i32>().unwrap())
        .clamp(0, *args[0].downcast_ref::<i32>().unwrap()))),
    |args| Some((Box::new(0_i32), Box::new(*args[0].downcast_ref::<i32>()?)))))?;
let rel = sheet.add_relationship(vec![Method::from_fn_1_1(a, b, |v: &i32| Ok(*v))])?;
let conditional = sheet.add_conditional(MatchExpr::cell(mode),
    vec![(vec![true], vec![rel])], vec![])?;
sheet.add_out(Method::from_fn_1_1(b, out, |v: &i32| Ok(*v)), vec![])?;
let check = sheet.add_requirement(b, Requirement::from_fn_1(b, |v: &i32| Ok(*v >= 0)))?;
sheet.validate()?;
sheet.write(a, 9_i32)?;
sheet.propagate()?;
assert_eq!(*sheet.read::<i32>(out)?, 9);
assert_eq!(*sheet.source::<i32>(a)?, 9);
assert_eq!(sheet.cell_type_id(a), Some(TypeId::of::<i32>()));
assert_eq!(sheet.cell_kind(out), Some(CellKind::Out));
assert_eq!(sheet.cells().count(), 5);
assert_eq!(sheet.relationships().count(), 2);
assert_eq!(sheet.cell_adj(b).unwrap().len(), 2);
assert_eq!(sheet.relationship_adj(rel), Some([a, b].as_slice()));
assert_eq!(sheet.method_inputs(rel, 0), Some([a].as_slice()));
assert_eq!(sheet.method_outputs(rel, 0), Some([b].as_slice()));
assert_eq!(sheet.selected_method(rel), Some(0));
assert!(sheet.is_source(a));
assert!(sheet.is_forced(b));
assert!(sheet.forced_cells().any(|id| id == b));
assert!(sheet.is_relationship_forced(rel));
assert!(sheet.forced_relationships().any(|id| id == rel));
assert_eq!(sheet.conditionals().collect::<Vec<_>>(), [conditional]);
assert_eq!(sheet.out_cells().collect::<Vec<_>>(), [out]);
assert_eq!(sheet.conditional_match_cells(conditional), Some([mode].as_slice()));
assert_eq!(sheet.conditional_branch_count(conditional), Some(1));
assert_eq!(sheet.conditional_branch_relationships(conditional, 0), Some([rel].as_slice()));
assert_eq!(sheet.conditional_default_relationships(conditional), Some([].as_slice()));
assert_eq!(sheet.conditional_active_branch(conditional)?, Some(0));
assert_eq!(sheet.filter_args(a), Some([limit].as_slice()));
assert_eq!(sheet.filter_dependents(limit), [a]);
assert!(matches!(sheet.filter_kind(a), Some(adam_rs::FilterKind::Range { .. })));
assert_eq!(sheet.filter_range::<i32>(a), Some((0, 100)));
assert!(sheet.filter_violation(a).is_none());
assert_eq!(sheet.filter_violated_cells().count(), 0);
assert!(sheet.filter_violation_cells().is_empty());
assert_eq!(sheet.cell_requirements(b), Some([check].as_slice()));
assert_eq!(sheet.requirement_cell(check), Some(b));
assert_eq!(sheet.requirement_inputs(check), Some([b].as_slice()));
assert!(sheet.cell_requirements_valid(b));
assert_eq!(sheet.violated_requirements(b).count(), 0);
assert!(sheet.contributing_cells(b).contains(&limit));
assert!(sheet.requirement_contributing_cells(check).contains(&a));
assert!(sheet.requirement_relevant_cells().contains(&a));
assert!(sheet.requirement_violation_cells().is_empty());
assert!(sheet.changed().any(|id| id == out));
sheet.clear_changed();
assert_eq!(sheet.changed().count(), 0);

§Forced, self-referencing, and reversible outcomes

A cell has three distinct outcomes when a relationship determines it:

  • Conditionally forced. The forcing relationship supplies the effective value, Sheet::read reports it, and the cell’s Sheet::source aspiration is unchanged. The cell keeps the priority it had when forcing began, and an explicit write while forced replaces that priority with the write’s recency. When the last forcing cause ends, the retained aspiration and priority compete in the first plan that can use the cell, so no additional edit is needed.
  • Self-referencing. A method that reads and writes the same cell seeds from its aspiration and honors that edit’s priority. Forcing and release do not erase either.
  • Ordinary reversible. A losing cell receives the winner’s value as its new aspiration with a derived priority. It does not revive an earlier value when the relationship later deactivates, and forcing a cell that was already derived retains that derived value and priority.
use adam_rs::{MatchExpr, Method, Sheet};
let mut sheet = Sheet::default();
let mode = sheet.add_cell(0_i32);
let input = sheet.add_cell(30_i32);
let a = sheet.add_cell(1_i32);
let b = sheet.add_cell(2_i32);
let force = sheet.add_relationship(vec![Method::from_fn_1_1(input, a, |v: &i32| Ok(*v))])?;
let reversible = sheet.add_relationship(vec![
    Method::from_fn_1_1(a, b, |v: &i32| Ok(*v)),
    Method::from_fn_1_1(b, a, |v: &i32| Ok(*v)),
])?;
sheet.add_conditional(
    MatchExpr::cell(mode),
    vec![(vec![1_i32], vec![force]), (vec![2_i32], vec![reversible])],
    vec![],
)?;
sheet.write(b, 22_i32)?;
sheet.write(a, 11_i32)?;
sheet.write(mode, 1_i32)?;
sheet.propagate()?;
assert!(sheet.is_forced(a));
assert_eq!((*sheet.read::<i32>(a)?, *sheet.source::<i32>(a)?), (30, 11));

// Releasing `a` keeps its newer edit, so `a` wins the reversible relationship.
sheet.write(mode, 2_i32)?;
sheet.propagate()?;
assert!(sheet.is_source(a));
assert_eq!(*sheet.read::<i32>(b)?, 11);

Structs§

Sheet
Owns a complete property model constraint graph.