1use crate::db::{
7 data::DataStore,
8 index::{IndexState, IndexStore},
9 journal::JournalTailStore,
10 schema::SchemaStore,
11};
12use candid::CandidType;
13use serde::Deserialize;
14use std::{cell::RefCell, thread::LocalKey};
15
16#[derive(Clone, Copy, Debug)]
26pub struct StoreHandle {
27 data: &'static LocalKey<RefCell<DataStore>>,
28 index: &'static LocalKey<RefCell<IndexStore>>,
29 schema: &'static LocalKey<RefCell<SchemaStore>>,
30 journal: Option<&'static LocalKey<RefCell<JournalTailStore>>>,
31 allocations: StoreAllocationIdentities,
32 capabilities: StoreRuntimeStorageCapabilities,
33}
34
35#[derive(Clone, Copy, Debug, Default, Deserialize, Eq, PartialEq)]
39pub enum StoreRuntimeStorageMode {
40 #[default]
42 Heap,
43 Journaled,
45}
46
47impl StoreRuntimeStorageMode {
48 #[must_use]
50 pub const fn as_str(self) -> &'static str {
51 match self {
52 Self::Heap => "heap",
53 Self::Journaled => "journaled",
54 }
55 }
56}
57
58#[derive(CandidType, Clone, Copy, Debug, Default, Deserialize, Eq, PartialEq)]
60pub enum StoreAllocationIdentityCapability {
61 #[default]
63 Present,
64 Absent,
66}
67
68impl StoreAllocationIdentityCapability {
69 #[must_use]
71 pub const fn as_str(self) -> &'static str {
72 match self {
73 Self::Present => "present",
74 Self::Absent => "absent",
75 }
76 }
77}
78
79#[derive(CandidType, Clone, Copy, Debug, Default, Deserialize, Eq, PartialEq)]
81pub enum StoreDurability {
82 #[default]
84 Durable,
85 Volatile,
87}
88
89impl StoreDurability {
90 #[must_use]
92 pub const fn as_str(self) -> &'static str {
93 match self {
94 Self::Durable => "durable",
95 Self::Volatile => "volatile",
96 }
97 }
98}
99
100#[derive(CandidType, Clone, Copy, Debug, Default, Deserialize, Eq, PartialEq)]
102pub enum StoreRecoveryCapability {
103 #[default]
106 StableBasePlusJournalReplay,
107 None,
109}
110
111impl StoreRecoveryCapability {
112 #[must_use]
114 pub const fn as_str(self) -> &'static str {
115 match self {
116 Self::StableBasePlusJournalReplay => "stable-base-plus-journal-replay",
117 Self::None => "none",
118 }
119 }
120}
121
122#[derive(CandidType, Clone, Copy, Debug, Default, Deserialize, Eq, PartialEq)]
124pub enum StoreCommitParticipation {
125 #[default]
127 Durable,
128 LiveOnly,
130}
131
132impl StoreCommitParticipation {
133 #[must_use]
135 pub const fn as_str(self) -> &'static str {
136 match self {
137 Self::Durable => "durable",
138 Self::LiveOnly => "live-only",
139 }
140 }
141}
142
143#[derive(CandidType, Clone, Copy, Debug, Default, Deserialize, Eq, PartialEq)]
145pub enum StoreSchemaMetadataCapability {
146 LiveRebuiltMetadata,
148 #[default]
150 CanonicalStableHistoryPlusJournalTail,
151}
152
153impl StoreSchemaMetadataCapability {
154 #[must_use]
156 pub const fn as_str(self) -> &'static str {
157 match self {
158 Self::LiveRebuiltMetadata => "live-rebuilt-metadata",
159 Self::CanonicalStableHistoryPlusJournalTail => {
160 "canonical-stable-history-plus-journal-tail"
161 }
162 }
163 }
164}
165
166#[derive(Clone, Copy, Debug, Default, Deserialize, Eq, PartialEq)]
168pub enum StoreRelationSourceCapability {
169 #[default]
171 DurableSource,
172 LiveSource,
174}
175
176#[derive(Clone, Copy, Debug, Default, Deserialize, Eq, PartialEq)]
178pub enum StoreRelationTargetCapability {
179 #[default]
181 DurableTarget,
182 VolatileTarget,
184}
185
186#[derive(Clone, Copy, Debug, Default, Deserialize, Eq, PartialEq)]
190pub struct StoreRuntimeStorageCapabilities {
191 storage_mode: StoreRuntimeStorageMode,
192 allocation_identity: StoreAllocationIdentityCapability,
193 durability: StoreDurability,
194 recovery: StoreRecoveryCapability,
195 commit_participation: StoreCommitParticipation,
196 schema_metadata: StoreSchemaMetadataCapability,
197 relation_source: StoreRelationSourceCapability,
198 relation_target: StoreRelationTargetCapability,
199}
200
201impl StoreRuntimeStorageCapabilities {
202 #[must_use]
204 pub const fn heap() -> Self {
205 Self {
206 storage_mode: StoreRuntimeStorageMode::Heap,
207 allocation_identity: StoreAllocationIdentityCapability::Absent,
208 durability: StoreDurability::Volatile,
209 recovery: StoreRecoveryCapability::None,
210 commit_participation: StoreCommitParticipation::LiveOnly,
211 schema_metadata: StoreSchemaMetadataCapability::LiveRebuiltMetadata,
212 relation_source: StoreRelationSourceCapability::LiveSource,
213 relation_target: StoreRelationTargetCapability::VolatileTarget,
214 }
215 }
216
217 #[must_use]
219 pub const fn journaled() -> Self {
220 Self {
221 storage_mode: StoreRuntimeStorageMode::Journaled,
222 allocation_identity: StoreAllocationIdentityCapability::Present,
223 durability: StoreDurability::Durable,
224 recovery: StoreRecoveryCapability::StableBasePlusJournalReplay,
225 commit_participation: StoreCommitParticipation::Durable,
226 schema_metadata: StoreSchemaMetadataCapability::CanonicalStableHistoryPlusJournalTail,
227 relation_source: StoreRelationSourceCapability::DurableSource,
228 relation_target: StoreRelationTargetCapability::DurableTarget,
229 }
230 }
231
232 #[must_use]
234 pub const fn storage_mode(self) -> StoreRuntimeStorageMode {
235 self.storage_mode
236 }
237
238 #[must_use]
240 pub const fn allocation_identity(self) -> StoreAllocationIdentityCapability {
241 self.allocation_identity
242 }
243
244 #[must_use]
246 pub const fn durability(self) -> StoreDurability {
247 self.durability
248 }
249
250 #[must_use]
252 pub const fn recovery(self) -> StoreRecoveryCapability {
253 self.recovery
254 }
255
256 #[must_use]
258 pub const fn commit_participation(self) -> StoreCommitParticipation {
259 self.commit_participation
260 }
261
262 #[must_use]
264 pub const fn schema_metadata(self) -> StoreSchemaMetadataCapability {
265 self.schema_metadata
266 }
267
268 #[must_use]
270 pub const fn relation_source(self) -> StoreRelationSourceCapability {
271 self.relation_source
272 }
273
274 #[must_use]
276 pub const fn relation_target(self) -> StoreRelationTargetCapability {
277 self.relation_target
278 }
279}
280
281#[derive(Clone, Copy, Debug, Eq, PartialEq)]
288pub struct StoreAllocationIdentity {
289 memory_id: u8,
290 stable_key: &'static str,
291}
292
293impl StoreAllocationIdentity {
294 #[must_use]
296 pub const fn new(memory_id: u8, stable_key: &'static str) -> Self {
297 Self {
298 memory_id,
299 stable_key,
300 }
301 }
302
303 #[must_use]
305 pub const fn memory_id(self) -> u8 {
306 self.memory_id
307 }
308
309 #[must_use]
311 pub const fn stable_key(self) -> &'static str {
312 self.stable_key
313 }
314}
315
316#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
324pub struct StoreAllocationIdentities {
325 data: Option<StoreAllocationIdentity>,
326 index: Option<StoreAllocationIdentity>,
327 schema: Option<StoreAllocationIdentity>,
328 journal: Option<StoreAllocationIdentity>,
329}
330
331impl StoreAllocationIdentities {
332 #[must_use]
334 pub const fn absent() -> Self {
335 Self {
336 data: None,
337 index: None,
338 schema: None,
339 journal: None,
340 }
341 }
342
343 #[must_use]
345 pub const fn new_journaled(
346 data: StoreAllocationIdentity,
347 index: StoreAllocationIdentity,
348 schema: StoreAllocationIdentity,
349 journal: StoreAllocationIdentity,
350 ) -> Self {
351 Self {
352 data: Some(data),
353 index: Some(index),
354 schema: Some(schema),
355 journal: Some(journal),
356 }
357 }
358
359 #[must_use]
361 pub const fn data(self) -> Option<StoreAllocationIdentity> {
362 self.data
363 }
364
365 #[must_use]
367 pub const fn index(self) -> Option<StoreAllocationIdentity> {
368 self.index
369 }
370
371 #[must_use]
373 pub const fn schema(self) -> Option<StoreAllocationIdentity> {
374 self.schema
375 }
376
377 #[must_use]
379 pub const fn journal(self) -> Option<StoreAllocationIdentity> {
380 self.journal
381 }
382
383 #[must_use]
386 pub const fn allocation_identity_capability(self) -> Option<StoreAllocationIdentityCapability> {
387 match (self.data, self.index, self.schema) {
388 (Some(_), Some(_), Some(_)) => Some(StoreAllocationIdentityCapability::Present),
389 (None, None, None) if self.journal.is_none() => {
390 Some(StoreAllocationIdentityCapability::Absent)
391 }
392 _ => None,
393 }
394 }
395
396 #[must_use]
399 pub const fn matches_storage_capabilities(
400 self,
401 capabilities: StoreRuntimeStorageCapabilities,
402 ) -> bool {
403 match capabilities.storage_mode() {
404 StoreRuntimeStorageMode::Heap => {
405 self.data.is_none()
406 && self.index.is_none()
407 && self.schema.is_none()
408 && self.journal.is_none()
409 }
410 StoreRuntimeStorageMode::Journaled => {
411 self.data.is_some()
412 && self.index.is_some()
413 && self.schema.is_some()
414 && self.journal.is_some()
415 }
416 }
417 }
418}
419
420impl StoreHandle {
421 #[must_use]
423 pub const fn new(
424 data: &'static LocalKey<RefCell<DataStore>>,
425 index: &'static LocalKey<RefCell<IndexStore>>,
426 schema: &'static LocalKey<RefCell<SchemaStore>>,
427 allocations: StoreAllocationIdentities,
428 capabilities: StoreRuntimeStorageCapabilities,
429 ) -> Self {
430 Self {
431 data,
432 index,
433 schema,
434 journal: None,
435 allocations,
436 capabilities,
437 }
438 }
439
440 #[must_use]
442 pub const fn new_journaled(
443 data: &'static LocalKey<RefCell<DataStore>>,
444 index: &'static LocalKey<RefCell<IndexStore>>,
445 schema: &'static LocalKey<RefCell<SchemaStore>>,
446 journal: &'static LocalKey<RefCell<JournalTailStore>>,
447 allocations: StoreAllocationIdentities,
448 capabilities: StoreRuntimeStorageCapabilities,
449 ) -> Self {
450 Self {
451 data,
452 index,
453 schema,
454 journal: Some(journal),
455 allocations,
456 capabilities,
457 }
458 }
459
460 pub fn with_data<R>(&self, f: impl FnOnce(&DataStore) -> R) -> R {
462 #[cfg(feature = "diagnostics")]
463 {
464 crate::db::physical_access::measure_physical_access_operation(|| {
465 self.data.with_borrow(f)
466 })
467 }
468
469 #[cfg(not(feature = "diagnostics"))]
470 {
471 self.data.with_borrow(f)
472 }
473 }
474
475 pub fn with_data_mut<R>(&self, f: impl FnOnce(&mut DataStore) -> R) -> R {
477 self.data.with_borrow_mut(f)
478 }
479
480 pub fn with_index<R>(&self, f: impl FnOnce(&IndexStore) -> R) -> R {
482 #[cfg(feature = "diagnostics")]
483 {
484 crate::db::physical_access::measure_physical_access_operation(|| {
485 self.index.with_borrow(f)
486 })
487 }
488
489 #[cfg(not(feature = "diagnostics"))]
490 {
491 self.index.with_borrow(f)
492 }
493 }
494
495 pub fn with_index_mut<R>(&self, f: impl FnOnce(&mut IndexStore) -> R) -> R {
497 self.index.with_borrow_mut(f)
498 }
499
500 pub fn with_schema<R>(&self, f: impl FnOnce(&SchemaStore) -> R) -> R {
502 self.schema.with_borrow(f)
503 }
504
505 pub fn with_schema_mut<R>(&self, f: impl FnOnce(&mut SchemaStore) -> R) -> R {
507 self.schema.with_borrow_mut(f)
508 }
509
510 #[must_use]
512 pub(in crate::db) fn index_state(&self) -> IndexState {
513 self.with_index(IndexStore::state)
514 }
515
516 pub(in crate::db) fn mark_index_building(&self) {
518 self.with_index_mut(IndexStore::mark_building);
519 }
520
521 pub(in crate::db) fn mark_index_ready(&self) {
523 self.with_index_mut(IndexStore::mark_ready);
524 }
525
526 #[must_use]
528 pub const fn data_store(&self) -> &'static LocalKey<RefCell<DataStore>> {
529 self.data
530 }
531
532 #[must_use]
534 pub const fn index_store(&self) -> &'static LocalKey<RefCell<IndexStore>> {
535 self.index
536 }
537
538 #[must_use]
540 pub const fn schema_store(&self) -> &'static LocalKey<RefCell<SchemaStore>> {
541 self.schema
542 }
543
544 #[must_use]
546 pub const fn journal_tail_store(&self) -> Option<&'static LocalKey<RefCell<JournalTailStore>>> {
547 self.journal
548 }
549
550 #[must_use]
553 pub const fn data_allocation(&self) -> Option<StoreAllocationIdentity> {
554 self.allocations.data()
555 }
556
557 #[must_use]
560 pub const fn index_allocation(&self) -> Option<StoreAllocationIdentity> {
561 self.allocations.index()
562 }
563
564 #[must_use]
567 pub const fn schema_allocation(&self) -> Option<StoreAllocationIdentity> {
568 self.allocations.schema()
569 }
570
571 #[must_use]
574 pub const fn journal_allocation(&self) -> Option<StoreAllocationIdentity> {
575 self.allocations.journal()
576 }
577
578 #[must_use]
580 pub(in crate::db) const fn allocation_identities(&self) -> StoreAllocationIdentities {
581 self.allocations
582 }
583
584 #[must_use]
586 pub const fn storage_capabilities(&self) -> StoreRuntimeStorageCapabilities {
587 self.capabilities
588 }
589}