adam_rs/cell.rs
1//! Value cells in the property model bipartite graph.
2//!
3//! Cells are accessed exclusively through [`crate::sheet::Sheet`].
4
5use std::any::{Any, TypeId};
6
7use slotmap::new_key_type;
8
9use crate::filter::FilterData;
10use crate::relationship::RelationshipId;
11use crate::requirement::RequirementId;
12
13new_key_type! {
14 /// A stable handle to a cell in a [`crate::sheet::Sheet`].
15 pub struct CellId;
16}
17
18/// A cell's fixed role in the planner's per-round source/derived assignment.
19#[derive(Debug, Clone, Copy, PartialEq, Eq)]
20pub enum CellKind {
21 /// May be a source or derived, chosen per round by the planner. Default kind.
22 Cell,
23 /// Always a source: never claimable as any method's output.
24 Source,
25 /// Always derived by exactly one fixed writer method; never `write()`-able.
26 Out,
27}
28
29/// Internal storage for a single value cell.
30pub(crate) struct CellData {
31 /// The value from the most recent `write()`/`add_cell`. Also written directly by
32 /// `Sheet::propagate` for outputs that aren't shadowed (the common case: an ordinary
33 /// derived cell behaves exactly as it did before `derived` existed). Left untouched by
34 /// `propagate()` only for outputs that *are* shadowed — self-referencing methods and
35 /// conditionally forced relationships write those into `derived` instead, which is what
36 /// keeps this field holding the original value for exactly those cells.
37 pub(crate) source: Box<dyn Any>,
38 /// The value most recently produced by a method this round, if this cell was
39 /// shadowed (a self-referencing output, or a pure output of a conditionally
40 /// registered relationship). Reset to `None` for every cell at the start of
41 /// every `Sheet::propagate` call, before planning begins.
42 pub(crate) derived: Option<Box<dyn Any>>,
43 /// The `TypeId` of the value, fixed at cell creation.
44 pub(crate) type_id: TypeId,
45 /// Write-recency strength. High-order bit (bit 63) is set for cells that have been
46 /// written or created via `add_cell`. Derived cells (outputs of selected methods)
47 /// receive strengths with bit 63 clear, assigned during the post-processing pass --
48 /// except a cell claimed *self-referencingly*, whose own written value is still the
49 /// authority behind the result and so keeps its explicit strength (see
50 /// `Sheet::post_process_strengths`).
51 pub(crate) strength: u64,
52 /// Set during `Sheet::propagate`; cleared by `Sheet::clear_changed`.
53 pub(crate) changed: bool,
54 /// Relationships that include this cell.
55 pub(crate) adj: Vec<RelationshipId>,
56 /// Type-erased equality: returns `true` iff both arguments hold equal values of the
57 /// cell's registered type. Captured at `add_cell` time from the concrete `T: PartialEq`.
58 pub(crate) eq_fn: fn(&dyn Any, &dyn Any) -> bool,
59 /// This cell's filter, if one is attached via `Sheet::add_filter`. At most one per
60 /// cell.
61 pub(crate) filter: Option<FilterData>,
62 /// This cell's fixed role in the planner's per-round source/derived assignment.
63 pub(crate) kind: CellKind,
64 /// This cell's requirements, in attachment order. Empty for most cells.
65 pub(crate) requirements: Vec<RequirementId>,
66}
67
68impl CellData {
69 /// Returns the effective current value: `derived` if present, else `source`.
70 pub(crate) fn effective(&self) -> &dyn Any {
71 self.derived.as_deref().unwrap_or(self.source.as_ref())
72 }
73
74 /// Returns whether `strength` reflects a live explicit `write()`/`add_cell` (bit 63
75 /// set) rather than a strength assigned by the post-round derived-strength pass.
76 pub(crate) fn has_explicit_strength(&self) -> bool {
77 self.strength & (1u64 << 63) != 0
78 }
79}
80
81#[cfg(test)]
82mod tests {
83 use super::*;
84
85 #[test]
86 fn cell_data_initial_state() {
87 let data = CellData {
88 source: Box::new(42_i32),
89 derived: None,
90 type_id: TypeId::of::<i32>(),
91 strength: 0,
92 changed: false,
93 adj: vec![],
94 eq_fn: |a, b| a.downcast_ref::<i32>() == b.downcast_ref::<i32>(),
95 filter: None,
96 kind: CellKind::Cell,
97 requirements: Vec::new(),
98 };
99 assert_eq!(data.type_id, TypeId::of::<i32>());
100 assert_eq!(data.strength, 0);
101 assert!(!data.changed);
102 assert!(data.adj.is_empty());
103 assert!(data.derived.is_none());
104 assert_eq!(*data.source.downcast_ref::<i32>().unwrap(), 42);
105 assert_eq!(*data.effective().downcast_ref::<i32>().unwrap(), 42);
106 assert_eq!(data.kind, CellKind::Cell);
107 assert!(data.requirements.is_empty());
108 let x: i32 = 42;
109 let y: i32 = 99;
110 assert!((data.eq_fn)(&x, &x));
111 assert!(!(data.eq_fn)(&x, &y));
112 }
113
114 #[test]
115 fn cell_id_is_copy() {
116 fn takes_copy<T: Copy>(_: T) {}
117 takes_copy(CellId::default());
118 }
119}