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}