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)]
188pub enum StoreLiveValidationCapability {
189 #[default]
191 Supported,
192}
193
194#[derive(Clone, Copy, Debug, Default, Deserialize, Eq, PartialEq)]
198pub struct StoreRuntimeStorageCapabilities {
199 storage_mode: StoreRuntimeStorageMode,
200 allocation_identity: StoreAllocationIdentityCapability,
201 durability: StoreDurability,
202 recovery: StoreRecoveryCapability,
203 commit_participation: StoreCommitParticipation,
204 schema_metadata: StoreSchemaMetadataCapability,
205 relation_source: StoreRelationSourceCapability,
206 relation_target: StoreRelationTargetCapability,
207 live_validation: StoreLiveValidationCapability,
208}
209
210impl StoreRuntimeStorageCapabilities {
211 #[must_use]
213 pub const fn heap() -> Self {
214 Self {
215 storage_mode: StoreRuntimeStorageMode::Heap,
216 allocation_identity: StoreAllocationIdentityCapability::Absent,
217 durability: StoreDurability::Volatile,
218 recovery: StoreRecoveryCapability::None,
219 commit_participation: StoreCommitParticipation::LiveOnly,
220 schema_metadata: StoreSchemaMetadataCapability::LiveRebuiltMetadata,
221 relation_source: StoreRelationSourceCapability::LiveSource,
222 relation_target: StoreRelationTargetCapability::VolatileTarget,
223 live_validation: StoreLiveValidationCapability::Supported,
224 }
225 }
226
227 #[must_use]
229 pub const fn journaled() -> Self {
230 Self {
231 storage_mode: StoreRuntimeStorageMode::Journaled,
232 allocation_identity: StoreAllocationIdentityCapability::Present,
233 durability: StoreDurability::Durable,
234 recovery: StoreRecoveryCapability::StableBasePlusJournalReplay,
235 commit_participation: StoreCommitParticipation::Durable,
236 schema_metadata: StoreSchemaMetadataCapability::CanonicalStableHistoryPlusJournalTail,
237 relation_source: StoreRelationSourceCapability::DurableSource,
238 relation_target: StoreRelationTargetCapability::DurableTarget,
239 live_validation: StoreLiveValidationCapability::Supported,
240 }
241 }
242
243 #[must_use]
245 pub const fn storage_mode(self) -> StoreRuntimeStorageMode {
246 self.storage_mode
247 }
248
249 #[must_use]
251 pub const fn allocation_identity(self) -> StoreAllocationIdentityCapability {
252 self.allocation_identity
253 }
254
255 #[must_use]
257 pub const fn durability(self) -> StoreDurability {
258 self.durability
259 }
260
261 #[must_use]
263 pub const fn recovery(self) -> StoreRecoveryCapability {
264 self.recovery
265 }
266
267 #[must_use]
269 pub const fn commit_participation(self) -> StoreCommitParticipation {
270 self.commit_participation
271 }
272
273 #[must_use]
275 pub const fn schema_metadata(self) -> StoreSchemaMetadataCapability {
276 self.schema_metadata
277 }
278
279 #[must_use]
281 pub const fn relation_source(self) -> StoreRelationSourceCapability {
282 self.relation_source
283 }
284
285 #[must_use]
287 pub const fn relation_target(self) -> StoreRelationTargetCapability {
288 self.relation_target
289 }
290
291 #[must_use]
293 pub const fn live_validation(self) -> StoreLiveValidationCapability {
294 self.live_validation
295 }
296}
297
298#[derive(Clone, Copy, Debug, Eq, PartialEq)]
305pub struct StoreAllocationIdentity {
306 memory_id: u8,
307 stable_key: &'static str,
308}
309
310impl StoreAllocationIdentity {
311 #[must_use]
313 pub const fn new(memory_id: u8, stable_key: &'static str) -> Self {
314 Self {
315 memory_id,
316 stable_key,
317 }
318 }
319
320 #[must_use]
322 pub const fn memory_id(self) -> u8 {
323 self.memory_id
324 }
325
326 #[must_use]
328 pub const fn stable_key(self) -> &'static str {
329 self.stable_key
330 }
331}
332
333#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
341pub struct StoreAllocationIdentities {
342 data: Option<StoreAllocationIdentity>,
343 index: Option<StoreAllocationIdentity>,
344 schema: Option<StoreAllocationIdentity>,
345 journal: Option<StoreAllocationIdentity>,
346}
347
348impl StoreAllocationIdentities {
349 #[must_use]
351 pub const fn absent() -> Self {
352 Self {
353 data: None,
354 index: None,
355 schema: None,
356 journal: None,
357 }
358 }
359
360 #[must_use]
362 pub const fn new_journaled(
363 data: StoreAllocationIdentity,
364 index: StoreAllocationIdentity,
365 schema: StoreAllocationIdentity,
366 journal: StoreAllocationIdentity,
367 ) -> Self {
368 Self {
369 data: Some(data),
370 index: Some(index),
371 schema: Some(schema),
372 journal: Some(journal),
373 }
374 }
375
376 #[must_use]
378 pub const fn data(self) -> Option<StoreAllocationIdentity> {
379 self.data
380 }
381
382 #[must_use]
384 pub const fn index(self) -> Option<StoreAllocationIdentity> {
385 self.index
386 }
387
388 #[must_use]
390 pub const fn schema(self) -> Option<StoreAllocationIdentity> {
391 self.schema
392 }
393
394 #[must_use]
396 pub const fn journal(self) -> Option<StoreAllocationIdentity> {
397 self.journal
398 }
399
400 #[must_use]
403 pub const fn allocation_identity_capability(self) -> Option<StoreAllocationIdentityCapability> {
404 match (self.data, self.index, self.schema) {
405 (Some(_), Some(_), Some(_)) => Some(StoreAllocationIdentityCapability::Present),
406 (None, None, None) if self.journal.is_none() => {
407 Some(StoreAllocationIdentityCapability::Absent)
408 }
409 _ => None,
410 }
411 }
412
413 #[must_use]
416 pub const fn matches_storage_capabilities(
417 self,
418 capabilities: StoreRuntimeStorageCapabilities,
419 ) -> bool {
420 match capabilities.storage_mode() {
421 StoreRuntimeStorageMode::Heap => {
422 self.data.is_none()
423 && self.index.is_none()
424 && self.schema.is_none()
425 && self.journal.is_none()
426 }
427 StoreRuntimeStorageMode::Journaled => {
428 self.data.is_some()
429 && self.index.is_some()
430 && self.schema.is_some()
431 && self.journal.is_some()
432 }
433 }
434 }
435}
436
437impl StoreHandle {
438 #[must_use]
440 pub const fn new(
441 data: &'static LocalKey<RefCell<DataStore>>,
442 index: &'static LocalKey<RefCell<IndexStore>>,
443 schema: &'static LocalKey<RefCell<SchemaStore>>,
444 allocations: StoreAllocationIdentities,
445 capabilities: StoreRuntimeStorageCapabilities,
446 ) -> Self {
447 Self {
448 data,
449 index,
450 schema,
451 journal: None,
452 allocations,
453 capabilities,
454 }
455 }
456
457 #[must_use]
459 pub const fn new_journaled(
460 data: &'static LocalKey<RefCell<DataStore>>,
461 index: &'static LocalKey<RefCell<IndexStore>>,
462 schema: &'static LocalKey<RefCell<SchemaStore>>,
463 journal: &'static LocalKey<RefCell<JournalTailStore>>,
464 allocations: StoreAllocationIdentities,
465 capabilities: StoreRuntimeStorageCapabilities,
466 ) -> Self {
467 Self {
468 data,
469 index,
470 schema,
471 journal: Some(journal),
472 allocations,
473 capabilities,
474 }
475 }
476
477 pub fn with_data<R>(&self, f: impl FnOnce(&DataStore) -> R) -> R {
479 #[cfg(feature = "diagnostics")]
480 {
481 crate::db::physical_access::measure_physical_access_operation(|| {
482 self.data.with_borrow(f)
483 })
484 }
485
486 #[cfg(not(feature = "diagnostics"))]
487 {
488 self.data.with_borrow(f)
489 }
490 }
491
492 pub fn with_data_mut<R>(&self, f: impl FnOnce(&mut DataStore) -> R) -> R {
494 self.data.with_borrow_mut(f)
495 }
496
497 pub fn with_index<R>(&self, f: impl FnOnce(&IndexStore) -> R) -> R {
499 #[cfg(feature = "diagnostics")]
500 {
501 crate::db::physical_access::measure_physical_access_operation(|| {
502 self.index.with_borrow(f)
503 })
504 }
505
506 #[cfg(not(feature = "diagnostics"))]
507 {
508 self.index.with_borrow(f)
509 }
510 }
511
512 pub fn with_index_mut<R>(&self, f: impl FnOnce(&mut IndexStore) -> R) -> R {
514 self.index.with_borrow_mut(f)
515 }
516
517 pub fn with_schema<R>(&self, f: impl FnOnce(&SchemaStore) -> R) -> R {
519 self.schema.with_borrow(f)
520 }
521
522 pub fn with_schema_mut<R>(&self, f: impl FnOnce(&mut SchemaStore) -> R) -> R {
524 self.schema.with_borrow_mut(f)
525 }
526
527 #[must_use]
529 pub(in crate::db) fn index_state(&self) -> IndexState {
530 self.with_index(IndexStore::state)
531 }
532
533 pub(in crate::db) fn mark_index_building(&self) {
535 self.with_index_mut(IndexStore::mark_building);
536 }
537
538 pub(in crate::db) fn mark_index_ready(&self) {
540 self.with_index_mut(IndexStore::mark_ready);
541 }
542
543 #[must_use]
545 pub const fn data_store(&self) -> &'static LocalKey<RefCell<DataStore>> {
546 self.data
547 }
548
549 #[must_use]
551 pub const fn index_store(&self) -> &'static LocalKey<RefCell<IndexStore>> {
552 self.index
553 }
554
555 #[must_use]
557 pub const fn schema_store(&self) -> &'static LocalKey<RefCell<SchemaStore>> {
558 self.schema
559 }
560
561 #[must_use]
563 pub const fn journal_tail_store(&self) -> Option<&'static LocalKey<RefCell<JournalTailStore>>> {
564 self.journal
565 }
566
567 #[must_use]
570 pub const fn data_allocation(&self) -> Option<StoreAllocationIdentity> {
571 self.allocations.data()
572 }
573
574 #[must_use]
577 pub const fn index_allocation(&self) -> Option<StoreAllocationIdentity> {
578 self.allocations.index()
579 }
580
581 #[must_use]
584 pub const fn schema_allocation(&self) -> Option<StoreAllocationIdentity> {
585 self.allocations.schema()
586 }
587
588 #[must_use]
591 pub const fn journal_allocation(&self) -> Option<StoreAllocationIdentity> {
592 self.allocations.journal()
593 }
594
595 #[must_use]
597 pub const fn storage_capabilities(&self) -> StoreRuntimeStorageCapabilities {
598 self.capabilities
599 }
600}