Skip to main content

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}