1use crate::db::{
2 data::DataStore,
3 index::{IndexState, IndexStore},
4 journal::JournalTailStore,
5 schema::SchemaStore,
6};
7use candid::CandidType;
8use serde::Deserialize;
9use std::{cell::RefCell, thread::LocalKey};
10
11#[derive(Clone, Copy, Debug)]
21pub struct StoreHandle {
22 data: &'static LocalKey<RefCell<DataStore>>,
23 index: &'static LocalKey<RefCell<IndexStore>>,
24 schema: &'static LocalKey<RefCell<SchemaStore>>,
25 journal: Option<&'static LocalKey<RefCell<JournalTailStore>>>,
26 allocations: StoreAllocationIdentities,
27 capabilities: StoreRuntimeStorageCapabilities,
28}
29
30#[derive(Clone, Copy, Debug, Default, Deserialize, Eq, PartialEq)]
34pub enum StoreRuntimeStorageMode {
35 #[default]
37 Heap,
38 Journaled,
40}
41
42impl StoreRuntimeStorageMode {
43 #[must_use]
45 pub const fn as_str(self) -> &'static str {
46 match self {
47 Self::Heap => "heap",
48 Self::Journaled => "journaled",
49 }
50 }
51}
52
53#[derive(CandidType, Clone, Copy, Debug, Default, Deserialize, Eq, PartialEq)]
55pub enum StoreAllocationIdentityCapability {
56 #[default]
58 Present,
59 Absent,
61}
62
63impl StoreAllocationIdentityCapability {
64 #[must_use]
66 pub const fn as_str(self) -> &'static str {
67 match self {
68 Self::Present => "present",
69 Self::Absent => "absent",
70 }
71 }
72}
73
74#[derive(CandidType, Clone, Copy, Debug, Default, Deserialize, Eq, PartialEq)]
76pub enum StoreDurability {
77 #[default]
79 Durable,
80 Volatile,
82}
83
84impl StoreDurability {
85 #[must_use]
87 pub const fn as_str(self) -> &'static str {
88 match self {
89 Self::Durable => "durable",
90 Self::Volatile => "volatile",
91 }
92 }
93}
94
95#[derive(CandidType, Clone, Copy, Debug, Default, Deserialize, Eq, PartialEq)]
97pub enum StoreRecoveryCapability {
98 #[default]
101 StableBasePlusJournalReplay,
102 None,
104}
105
106impl StoreRecoveryCapability {
107 #[must_use]
109 pub const fn as_str(self) -> &'static str {
110 match self {
111 Self::StableBasePlusJournalReplay => "stable-base-plus-journal-replay",
112 Self::None => "none",
113 }
114 }
115}
116
117#[derive(CandidType, Clone, Copy, Debug, Default, Deserialize, Eq, PartialEq)]
119pub enum StoreCommitParticipation {
120 #[default]
122 Durable,
123 LiveOnly,
125}
126
127impl StoreCommitParticipation {
128 #[must_use]
130 pub const fn as_str(self) -> &'static str {
131 match self {
132 Self::Durable => "durable",
133 Self::LiveOnly => "live-only",
134 }
135 }
136}
137
138#[derive(CandidType, Clone, Copy, Debug, Default, Deserialize, Eq, PartialEq)]
140pub enum StoreSchemaMetadataCapability {
141 LiveRebuiltMetadata,
143 #[default]
145 CanonicalStableHistoryPlusJournalTail,
146}
147
148impl StoreSchemaMetadataCapability {
149 #[must_use]
151 pub const fn as_str(self) -> &'static str {
152 match self {
153 Self::LiveRebuiltMetadata => "live-rebuilt-metadata",
154 Self::CanonicalStableHistoryPlusJournalTail => {
155 "canonical-stable-history-plus-journal-tail"
156 }
157 }
158 }
159}
160
161#[derive(Clone, Copy, Debug, Default, Deserialize, Eq, PartialEq)]
163pub enum StoreRelationSourceCapability {
164 #[default]
166 DurableSource,
167 LiveSource,
169}
170
171#[derive(Clone, Copy, Debug, Default, Deserialize, Eq, PartialEq)]
173pub enum StoreRelationTargetCapability {
174 #[default]
176 DurableTarget,
177 VolatileTarget,
179}
180
181#[derive(Clone, Copy, Debug, Default, Deserialize, Eq, PartialEq)]
183pub enum StoreLiveValidationCapability {
184 #[default]
186 Supported,
187}
188
189#[derive(Clone, Copy, Debug, Default, Deserialize, Eq, PartialEq)]
193pub struct StoreRuntimeStorageCapabilities {
194 storage_mode: StoreRuntimeStorageMode,
195 allocation_identity: StoreAllocationIdentityCapability,
196 durability: StoreDurability,
197 recovery: StoreRecoveryCapability,
198 commit_participation: StoreCommitParticipation,
199 schema_metadata: StoreSchemaMetadataCapability,
200 relation_source: StoreRelationSourceCapability,
201 relation_target: StoreRelationTargetCapability,
202 live_validation: StoreLiveValidationCapability,
203}
204
205impl StoreRuntimeStorageCapabilities {
206 #[must_use]
208 pub const fn heap() -> Self {
209 Self {
210 storage_mode: StoreRuntimeStorageMode::Heap,
211 allocation_identity: StoreAllocationIdentityCapability::Absent,
212 durability: StoreDurability::Volatile,
213 recovery: StoreRecoveryCapability::None,
214 commit_participation: StoreCommitParticipation::LiveOnly,
215 schema_metadata: StoreSchemaMetadataCapability::LiveRebuiltMetadata,
216 relation_source: StoreRelationSourceCapability::LiveSource,
217 relation_target: StoreRelationTargetCapability::VolatileTarget,
218 live_validation: StoreLiveValidationCapability::Supported,
219 }
220 }
221
222 #[must_use]
224 pub const fn journaled() -> Self {
225 Self {
226 storage_mode: StoreRuntimeStorageMode::Journaled,
227 allocation_identity: StoreAllocationIdentityCapability::Present,
228 durability: StoreDurability::Durable,
229 recovery: StoreRecoveryCapability::StableBasePlusJournalReplay,
230 commit_participation: StoreCommitParticipation::Durable,
231 schema_metadata: StoreSchemaMetadataCapability::CanonicalStableHistoryPlusJournalTail,
232 relation_source: StoreRelationSourceCapability::DurableSource,
233 relation_target: StoreRelationTargetCapability::DurableTarget,
234 live_validation: StoreLiveValidationCapability::Supported,
235 }
236 }
237
238 #[must_use]
240 pub const fn storage_mode(self) -> StoreRuntimeStorageMode {
241 self.storage_mode
242 }
243
244 #[must_use]
246 pub const fn allocation_identity(self) -> StoreAllocationIdentityCapability {
247 self.allocation_identity
248 }
249
250 #[must_use]
252 pub const fn durability(self) -> StoreDurability {
253 self.durability
254 }
255
256 #[must_use]
258 pub const fn recovery(self) -> StoreRecoveryCapability {
259 self.recovery
260 }
261
262 #[must_use]
264 pub const fn commit_participation(self) -> StoreCommitParticipation {
265 self.commit_participation
266 }
267
268 #[must_use]
270 pub const fn schema_metadata(self) -> StoreSchemaMetadataCapability {
271 self.schema_metadata
272 }
273
274 #[must_use]
276 pub const fn relation_source(self) -> StoreRelationSourceCapability {
277 self.relation_source
278 }
279
280 #[must_use]
282 pub const fn relation_target(self) -> StoreRelationTargetCapability {
283 self.relation_target
284 }
285
286 #[must_use]
288 pub const fn live_validation(self) -> StoreLiveValidationCapability {
289 self.live_validation
290 }
291}
292
293#[derive(Clone, Copy, Debug, Eq, PartialEq)]
300pub struct StoreAllocationIdentity {
301 memory_id: u8,
302 stable_key: &'static str,
303}
304
305impl StoreAllocationIdentity {
306 #[must_use]
308 pub const fn new(memory_id: u8, stable_key: &'static str) -> Self {
309 Self {
310 memory_id,
311 stable_key,
312 }
313 }
314
315 #[must_use]
317 pub const fn memory_id(self) -> u8 {
318 self.memory_id
319 }
320
321 #[must_use]
323 pub const fn stable_key(self) -> &'static str {
324 self.stable_key
325 }
326}
327
328#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
336pub struct StoreAllocationIdentities {
337 data: Option<StoreAllocationIdentity>,
338 index: Option<StoreAllocationIdentity>,
339 schema: Option<StoreAllocationIdentity>,
340 journal: Option<StoreAllocationIdentity>,
341}
342
343impl StoreAllocationIdentities {
344 #[must_use]
346 pub const fn absent() -> Self {
347 Self {
348 data: None,
349 index: None,
350 schema: None,
351 journal: None,
352 }
353 }
354
355 #[must_use]
357 pub const fn new_journaled(
358 data: StoreAllocationIdentity,
359 index: StoreAllocationIdentity,
360 schema: StoreAllocationIdentity,
361 journal: StoreAllocationIdentity,
362 ) -> Self {
363 Self {
364 data: Some(data),
365 index: Some(index),
366 schema: Some(schema),
367 journal: Some(journal),
368 }
369 }
370
371 #[must_use]
373 pub const fn data(self) -> Option<StoreAllocationIdentity> {
374 self.data
375 }
376
377 #[must_use]
379 pub const fn index(self) -> Option<StoreAllocationIdentity> {
380 self.index
381 }
382
383 #[must_use]
385 pub const fn schema(self) -> Option<StoreAllocationIdentity> {
386 self.schema
387 }
388
389 #[must_use]
391 pub const fn journal(self) -> Option<StoreAllocationIdentity> {
392 self.journal
393 }
394
395 #[must_use]
398 pub const fn allocation_identity_capability(self) -> Option<StoreAllocationIdentityCapability> {
399 match (self.data, self.index, self.schema) {
400 (Some(_), Some(_), Some(_)) => Some(StoreAllocationIdentityCapability::Present),
401 (None, None, None) if self.journal.is_none() => {
402 Some(StoreAllocationIdentityCapability::Absent)
403 }
404 _ => None,
405 }
406 }
407
408 #[must_use]
411 pub const fn matches_storage_capabilities(
412 self,
413 capabilities: StoreRuntimeStorageCapabilities,
414 ) -> bool {
415 match capabilities.storage_mode() {
416 StoreRuntimeStorageMode::Heap => {
417 self.data.is_none()
418 && self.index.is_none()
419 && self.schema.is_none()
420 && self.journal.is_none()
421 }
422 StoreRuntimeStorageMode::Journaled => {
423 self.data.is_some()
424 && self.index.is_some()
425 && self.schema.is_some()
426 && self.journal.is_some()
427 }
428 }
429 }
430}
431
432impl StoreHandle {
433 #[must_use]
435 pub const fn new(
436 data: &'static LocalKey<RefCell<DataStore>>,
437 index: &'static LocalKey<RefCell<IndexStore>>,
438 schema: &'static LocalKey<RefCell<SchemaStore>>,
439 allocations: StoreAllocationIdentities,
440 capabilities: StoreRuntimeStorageCapabilities,
441 ) -> Self {
442 Self {
443 data,
444 index,
445 schema,
446 journal: None,
447 allocations,
448 capabilities,
449 }
450 }
451
452 #[must_use]
454 pub const fn new_journaled(
455 data: &'static LocalKey<RefCell<DataStore>>,
456 index: &'static LocalKey<RefCell<IndexStore>>,
457 schema: &'static LocalKey<RefCell<SchemaStore>>,
458 journal: &'static LocalKey<RefCell<JournalTailStore>>,
459 allocations: StoreAllocationIdentities,
460 capabilities: StoreRuntimeStorageCapabilities,
461 ) -> Self {
462 Self {
463 data,
464 index,
465 schema,
466 journal: Some(journal),
467 allocations,
468 capabilities,
469 }
470 }
471
472 pub fn with_data<R>(&self, f: impl FnOnce(&DataStore) -> R) -> R {
474 #[cfg(feature = "diagnostics")]
475 {
476 crate::db::physical_access::measure_physical_access_operation(|| {
477 self.data.with_borrow(f)
478 })
479 }
480
481 #[cfg(not(feature = "diagnostics"))]
482 {
483 self.data.with_borrow(f)
484 }
485 }
486
487 pub fn with_data_mut<R>(&self, f: impl FnOnce(&mut DataStore) -> R) -> R {
489 self.data.with_borrow_mut(f)
490 }
491
492 pub fn with_index<R>(&self, f: impl FnOnce(&IndexStore) -> R) -> R {
494 #[cfg(feature = "diagnostics")]
495 {
496 crate::db::physical_access::measure_physical_access_operation(|| {
497 self.index.with_borrow(f)
498 })
499 }
500
501 #[cfg(not(feature = "diagnostics"))]
502 {
503 self.index.with_borrow(f)
504 }
505 }
506
507 pub fn with_index_mut<R>(&self, f: impl FnOnce(&mut IndexStore) -> R) -> R {
509 self.index.with_borrow_mut(f)
510 }
511
512 pub fn with_schema<R>(&self, f: impl FnOnce(&SchemaStore) -> R) -> R {
514 self.schema.with_borrow(f)
515 }
516
517 pub fn with_schema_mut<R>(&self, f: impl FnOnce(&mut SchemaStore) -> R) -> R {
519 self.schema.with_borrow_mut(f)
520 }
521
522 #[must_use]
524 pub(in crate::db) fn index_state(&self) -> IndexState {
525 self.with_index(IndexStore::state)
526 }
527
528 pub(in crate::db) fn mark_index_building(&self) {
530 self.with_index_mut(IndexStore::mark_building);
531 }
532
533 pub(in crate::db) fn mark_index_ready(&self) {
535 self.with_index_mut(IndexStore::mark_ready);
536 }
537
538 #[must_use]
540 pub const fn data_store(&self) -> &'static LocalKey<RefCell<DataStore>> {
541 self.data
542 }
543
544 #[must_use]
546 pub const fn index_store(&self) -> &'static LocalKey<RefCell<IndexStore>> {
547 self.index
548 }
549
550 #[must_use]
552 pub const fn schema_store(&self) -> &'static LocalKey<RefCell<SchemaStore>> {
553 self.schema
554 }
555
556 #[must_use]
558 pub const fn journal_tail_store(&self) -> Option<&'static LocalKey<RefCell<JournalTailStore>>> {
559 self.journal
560 }
561
562 #[must_use]
565 pub const fn data_allocation(&self) -> Option<StoreAllocationIdentity> {
566 self.allocations.data()
567 }
568
569 #[must_use]
572 pub const fn index_allocation(&self) -> Option<StoreAllocationIdentity> {
573 self.allocations.index()
574 }
575
576 #[must_use]
579 pub const fn schema_allocation(&self) -> Option<StoreAllocationIdentity> {
580 self.allocations.schema()
581 }
582
583 #[must_use]
586 pub const fn journal_allocation(&self) -> Option<StoreAllocationIdentity> {
587 self.allocations.journal()
588 }
589
590 #[must_use]
592 pub const fn storage_capabilities(&self) -> StoreRuntimeStorageCapabilities {
593 self.capabilities
594 }
595}