cranpose_core/
slot_storage.rs

1//! Abstract slot storage trait and related types.
2//!
3//! This module defines the high-level interface that all slot storage backends
4//! must implement. The `Composer` and composition engine interact exclusively
5//! through this trait, allowing different storage strategies (gap buffers,
6//! chunked storage, hierarchical, split layout/payload, etc.) to be used
7//! interchangeably.
8
9use crate::{Key, NodeId, Owned, ScopeId};
10
11/// Opaque handle to a group in the slot storage.
12#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
13pub struct GroupId(pub(crate) usize);
14
15impl GroupId {
16    pub(crate) fn new(index: usize) -> Self {
17        Self(index)
18    }
19
20    pub(crate) fn index(&self) -> usize {
21        self.0
22    }
23}
24
25/// Opaque handle to a value slot in the slot storage.
26#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
27pub struct ValueSlotId(pub(crate) usize);
28
29impl ValueSlotId {
30    pub(crate) fn new(index: usize) -> Self {
31        Self(index)
32    }
33
34    pub(crate) fn index(&self) -> usize {
35        self.0
36    }
37}
38
39/// Result of starting a group.
40pub struct StartGroup<G> {
41    pub group: G,
42    /// True if this group was restored from a gap (unstable children).
43    pub restored_from_gap: bool,
44}
45
46/// Abstract slot API that the composer / composition engine talks to.
47/// Concrete backends (SlotTable with gap buffer, chunked storage, arena, etc.)
48/// implement this and can keep whatever internal layout they want.
49pub trait SlotStorage {
50    /// Opaque handle to a started group.
51    type Group: Copy + Eq;
52    /// Opaque handle to a value slot.
53    type ValueSlot: Copy + Eq;
54
55    // ── groups ──────────────────────────────────────────────────────────────
56
57    /// Begin a group with the given key.
58    ///
59    /// Returns a handle to the group and whether it was restored from a gap
60    /// (which means the composer needs to force-recompose the scope).
61    fn begin_group(&mut self, key: Key) -> StartGroup<Self::Group>;
62
63    /// Associate the runtime recomposition scope with this group.
64    fn set_group_scope(&mut self, group: Self::Group, scope: ScopeId);
65
66    /// End the current group.
67    fn end_group(&mut self);
68
69    /// Skip over the current group (used by the "skip optimization" in the macro).
70    fn skip_current_group(&mut self);
71
72    /// Return node ids that live in the current group (needed so the composer
73    /// can reattach them to the parent when skipping).
74    fn nodes_in_current_group(&self) -> Vec<NodeId>;
75
76    // ── recomposition ───────────────────────────────────────────────────────
77
78    /// Start recomposing the group that owns `scope`. Returns the group we
79    /// started, or `None` if that scope is gone.
80    fn begin_recranpose_at_scope(&mut self, scope: ScopeId) -> Option<Self::Group>;
81
82    /// Finish the recomposition started with `begin_recranpose_at_scope`.
83    fn end_recompose(&mut self);
84
85    // ── values / remember ───────────────────────────────────────────────────
86
87    /// Allocate or reuse a value slot at the current cursor.
88    fn alloc_value_slot<T: 'static>(&mut self, init: impl FnOnce() -> T) -> Self::ValueSlot;
89
90    /// Immutable read of a value slot.
91    fn read_value<T: 'static>(&self, slot: Self::ValueSlot) -> &T;
92
93    /// Mutable read of a value slot.
94    fn read_value_mut<T: 'static>(&mut self, slot: Self::ValueSlot) -> &mut T;
95
96    /// Overwrite an existing value slot.
97    fn write_value<T: 'static>(&mut self, slot: Self::ValueSlot, value: T);
98
99    /// Convenience "remember" built on top of value slots.
100    fn remember<T: 'static>(&mut self, init: impl FnOnce() -> T) -> Owned<T>;
101
102    // ── nodes ──────────────────────────────────────────────────────────────
103
104    /// Peek a node at the current cursor (don't advance).
105    fn peek_node(&self) -> Option<NodeId>;
106
107    /// Record a node at the current cursor (and advance).
108    fn record_node(&mut self, id: NodeId);
109
110    /// Advance after we've read a node via the applier path.
111    fn advance_after_node_read(&mut self);
112
113    /// Step the cursor back by one (used when we probed and need to overwrite).
114    fn step_back(&mut self);
115
116    // ── lifecycle / cleanup ─────────────────────────────────────────────────
117
118    /// "Finalize" the current group: mark unreachable tail as gaps.
119    /// Returns `true` if we marked gaps (which means children are unstable).
120    fn finalize_current_group(&mut self) -> bool;
121
122    /// Reset to the beginning (used by subcompose + top-level render).
123    fn reset(&mut self);
124
125    /// Flush any deferred anchor rebuilds.
126    fn flush(&mut self);
127}