Skip to main content

ic_memory/
session.rs

1use crate::{
2    declaration::AllocationDeclaration, key::StableKey, slot::AllocationSlotDescriptor,
3    substrate::StorageSubstrate,
4};
5use std::sync::Arc;
6
7///
8/// ValidatedAllocations
9///
10/// Allocation declarations accepted by policy and historical ledger validation.
11///
12/// This value is produced by [`crate::validate_allocations`] and is the bridge
13/// between declaration validation and opening storage. It is not a durable
14/// ledger record; staging commits it into the next generation before an
15/// integration should expose memory handles.
16///
17/// This is an in-memory capability, not a serde DTO. It has no public
18/// constructor and should only be produced by validation or bootstrap paths.
19///
20
21#[derive(Clone, Debug, Eq, PartialEq)]
22pub struct ValidatedAllocations {
23    inner: Arc<ValidatedState>,
24    _private: (),
25}
26
27#[derive(Clone, Debug, Eq, PartialEq)]
28struct ValidatedState {
29    /// Committed generation that validated these allocations.
30    generation: u64,
31    /// Validated declarations.
32    declarations: Vec<AllocationDeclaration>,
33    /// Optional binary/runtime identity for generation diagnostics.
34    runtime_fingerprint: Option<String>,
35}
36
37impl ValidatedAllocations {
38    pub(crate) fn new(
39        generation: u64,
40        declarations: Vec<AllocationDeclaration>,
41        runtime_fingerprint: Option<String>,
42    ) -> Self {
43        Self {
44            inner: Arc::new(ValidatedState {
45                generation,
46                declarations,
47                runtime_fingerprint,
48            }),
49            _private: (),
50        }
51    }
52
53    pub(crate) fn with_generation(self, generation: u64) -> Self {
54        let mut state = (*self.inner).clone();
55        state.generation = generation;
56        Self {
57            inner: Arc::new(state),
58            _private: (),
59        }
60    }
61
62    pub(crate) fn without_stable_key(self, stable_key: &str) -> Self {
63        let mut state = (*self.inner).clone();
64        state
65            .declarations
66            .retain(|declaration| declaration.stable_key.as_str() != stable_key);
67        Self {
68            inner: Arc::new(state),
69            _private: (),
70        }
71    }
72
73    /// Return the committed generation that validated these allocations.
74    #[must_use]
75    pub fn generation(&self) -> u64 {
76        self.inner.generation
77    }
78
79    /// Borrow the validated declarations.
80    #[must_use]
81    pub fn declarations(&self) -> &[AllocationDeclaration] {
82        &self.inner.declarations
83    }
84
85    /// Borrow the optional runtime fingerprint.
86    #[must_use]
87    pub fn runtime_fingerprint(&self) -> Option<&str> {
88        self.inner.runtime_fingerprint.as_deref()
89    }
90
91    /// Find a validated slot by stable key.
92    #[must_use]
93    pub fn slot_for(&self, key: &StableKey) -> Option<&AllocationSlotDescriptor> {
94        self.declarations()
95            .iter()
96            .find(|declaration| &declaration.stable_key == key)
97            .map(|declaration| &declaration.slot)
98    }
99}
100
101///
102/// AllocationSession
103///
104/// Validated capability required before opening allocation slots.
105///
106/// Integrations should construct sessions only after recovering the ledger,
107/// validating declarations, and committing the next generation. Opening storage
108/// through this type keeps handle creation tied to the validated stable-key
109/// snapshot.
110pub struct AllocationSession<S: StorageSubstrate> {
111    substrate: S,
112    validated: ValidatedAllocations,
113}
114
115impl<S: StorageSubstrate> AllocationSession<S> {
116    /// Construct a session from a substrate and validated allocation set.
117    #[must_use]
118    pub const fn new(substrate: S, validated: ValidatedAllocations) -> Self {
119        Self {
120            substrate,
121            validated,
122        }
123    }
124
125    /// Borrow the validated allocation set.
126    #[must_use]
127    pub const fn validated(&self) -> &ValidatedAllocations {
128        &self.validated
129    }
130
131    /// Open an allocation by stable key.
132    pub fn open(
133        &self,
134        key: &StableKey,
135    ) -> Result<S::MemoryHandle, AllocationSessionError<S::Error>> {
136        let slot = self
137            .validated
138            .slot_for(key)
139            .ok_or_else(|| AllocationSessionError::UnknownStableKey(key.clone()))?;
140        self.substrate
141            .open_slot(slot)
142            .map_err(AllocationSessionError::Substrate)
143    }
144}
145
146///
147/// AllocationSessionError
148///
149/// Failure to open through a validated allocation session.
150#[non_exhaustive]
151#[derive(Clone, Debug, Eq, thiserror::Error, PartialEq)]
152pub enum AllocationSessionError<E> {
153    /// Stable key was not part of the validated allocation snapshot.
154    #[error("stable key '{0}' was not validated for this allocation session")]
155    UnknownStableKey(StableKey),
156    /// Storage substrate failed to open the validated slot.
157    #[error("storage substrate failed to open allocation slot")]
158    Substrate(E),
159}