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