Skip to main content

icydb_core/db/diagnostics/
model.rs

1//! Module: diagnostics::model
2//! Responsibility: diagnostics report DTO contracts and simple accessors.
3//! Does not own: store traversal, integrity scanning, or execution trace policy.
4//! Boundary: report assembly modules construct these DTOs; public callers read them.
5
6use crate::db::index::IndexState;
7use candid::CandidType;
8use serde::Deserialize;
9
10#[cfg_attr(doc, doc = "StorageReport\n\nLive storage snapshot payload.")]
11#[derive(CandidType, Clone, Debug, Default, Deserialize)]
12pub struct StorageReport {
13    pub(crate) storage_data: Vec<DataStoreSnapshot>,
14    pub(crate) storage_index: Vec<IndexStoreSnapshot>,
15    pub(crate) entity_storage: Vec<EntitySnapshot>,
16    pub(crate) corrupted_keys: u64,
17    pub(crate) corrupted_entries: u64,
18}
19
20#[cfg_attr(
21    doc,
22    doc = "IntegrityTotals\n\nAggregated integrity-scan counters across all stores."
23)]
24#[derive(CandidType, Clone, Debug, Default, Deserialize)]
25pub struct IntegrityTotals {
26    pub(crate) data_rows_scanned: u64,
27    pub(crate) index_entries_scanned: u64,
28    pub(crate) corrupted_data_keys: u64,
29    pub(crate) corrupted_data_rows: u64,
30    pub(crate) corrupted_index_keys: u64,
31    pub(crate) corrupted_index_entries: u64,
32    pub(crate) missing_index_entries: u64,
33    pub(crate) divergent_index_entries: u64,
34    pub(crate) orphan_index_references: u64,
35    pub(crate) misuse_findings: u64,
36}
37
38impl IntegrityTotals {
39    pub(super) const fn add_store_snapshot(&mut self, store: &IntegrityStoreSnapshot) {
40        self.data_rows_scanned = self
41            .data_rows_scanned
42            .saturating_add(store.data_rows_scanned);
43        self.index_entries_scanned = self
44            .index_entries_scanned
45            .saturating_add(store.index_entries_scanned);
46        self.corrupted_data_keys = self
47            .corrupted_data_keys
48            .saturating_add(store.corrupted_data_keys);
49        self.corrupted_data_rows = self
50            .corrupted_data_rows
51            .saturating_add(store.corrupted_data_rows);
52        self.corrupted_index_keys = self
53            .corrupted_index_keys
54            .saturating_add(store.corrupted_index_keys);
55        self.corrupted_index_entries = self
56            .corrupted_index_entries
57            .saturating_add(store.corrupted_index_entries);
58        self.missing_index_entries = self
59            .missing_index_entries
60            .saturating_add(store.missing_index_entries);
61        self.divergent_index_entries = self
62            .divergent_index_entries
63            .saturating_add(store.divergent_index_entries);
64        self.orphan_index_references = self
65            .orphan_index_references
66            .saturating_add(store.orphan_index_references);
67        self.misuse_findings = self.misuse_findings.saturating_add(store.misuse_findings);
68    }
69
70    /// Return total number of data rows scanned.
71    #[must_use]
72    pub const fn data_rows_scanned(&self) -> u64 {
73        self.data_rows_scanned
74    }
75
76    /// Return total number of index entries scanned.
77    #[must_use]
78    pub const fn index_entries_scanned(&self) -> u64 {
79        self.index_entries_scanned
80    }
81
82    /// Return total number of corrupted data-key findings.
83    #[must_use]
84    pub const fn corrupted_data_keys(&self) -> u64 {
85        self.corrupted_data_keys
86    }
87
88    /// Return total number of corrupted data-row findings.
89    #[must_use]
90    pub const fn corrupted_data_rows(&self) -> u64 {
91        self.corrupted_data_rows
92    }
93
94    /// Return total number of corrupted index-key findings.
95    #[must_use]
96    pub const fn corrupted_index_keys(&self) -> u64 {
97        self.corrupted_index_keys
98    }
99
100    /// Return total number of corrupted index-entry findings.
101    #[must_use]
102    pub const fn corrupted_index_entries(&self) -> u64 {
103        self.corrupted_index_entries
104    }
105
106    /// Return total number of missing index-entry findings.
107    #[must_use]
108    pub const fn missing_index_entries(&self) -> u64 {
109        self.missing_index_entries
110    }
111
112    /// Return total number of divergent index-entry findings.
113    #[must_use]
114    pub const fn divergent_index_entries(&self) -> u64 {
115        self.divergent_index_entries
116    }
117
118    /// Return total number of orphan index-reference findings.
119    #[must_use]
120    pub const fn orphan_index_references(&self) -> u64 {
121        self.orphan_index_references
122    }
123
124    /// Return total number of misuse findings.
125    #[must_use]
126    pub const fn misuse_findings(&self) -> u64 {
127        self.misuse_findings
128    }
129}
130
131#[cfg_attr(
132    doc,
133    doc = "IntegrityStoreSnapshot\n\nPer-store integrity findings and scan counters."
134)]
135#[derive(CandidType, Clone, Debug, Default, Deserialize)]
136pub struct IntegrityStoreSnapshot {
137    pub(crate) path: String,
138    pub(crate) data_rows_scanned: u64,
139    pub(crate) index_entries_scanned: u64,
140    pub(crate) corrupted_data_keys: u64,
141    pub(crate) corrupted_data_rows: u64,
142    pub(crate) corrupted_index_keys: u64,
143    pub(crate) corrupted_index_entries: u64,
144    pub(crate) missing_index_entries: u64,
145    pub(crate) divergent_index_entries: u64,
146    pub(crate) orphan_index_references: u64,
147    pub(crate) misuse_findings: u64,
148}
149
150impl IntegrityStoreSnapshot {
151    /// Construct one empty store-level integrity snapshot.
152    #[must_use]
153    pub(crate) fn new(path: String) -> Self {
154        Self {
155            path,
156            ..Self::default()
157        }
158    }
159
160    /// Borrow store path.
161    #[must_use]
162    pub const fn path(&self) -> &str {
163        self.path.as_str()
164    }
165
166    /// Return number of scanned data rows.
167    #[must_use]
168    pub const fn data_rows_scanned(&self) -> u64 {
169        self.data_rows_scanned
170    }
171
172    /// Return number of scanned index entries.
173    #[must_use]
174    pub const fn index_entries_scanned(&self) -> u64 {
175        self.index_entries_scanned
176    }
177
178    /// Return number of corrupted data-key findings.
179    #[must_use]
180    pub const fn corrupted_data_keys(&self) -> u64 {
181        self.corrupted_data_keys
182    }
183
184    /// Return number of corrupted data-row findings.
185    #[must_use]
186    pub const fn corrupted_data_rows(&self) -> u64 {
187        self.corrupted_data_rows
188    }
189
190    /// Return number of corrupted index-key findings.
191    #[must_use]
192    pub const fn corrupted_index_keys(&self) -> u64 {
193        self.corrupted_index_keys
194    }
195
196    /// Return number of corrupted index-entry findings.
197    #[must_use]
198    pub const fn corrupted_index_entries(&self) -> u64 {
199        self.corrupted_index_entries
200    }
201
202    /// Return number of missing index-entry findings.
203    #[must_use]
204    pub const fn missing_index_entries(&self) -> u64 {
205        self.missing_index_entries
206    }
207
208    /// Return number of divergent index-entry findings.
209    #[must_use]
210    pub const fn divergent_index_entries(&self) -> u64 {
211        self.divergent_index_entries
212    }
213
214    /// Return number of orphan index-reference findings.
215    #[must_use]
216    pub const fn orphan_index_references(&self) -> u64 {
217        self.orphan_index_references
218    }
219
220    /// Return number of misuse findings.
221    #[must_use]
222    pub const fn misuse_findings(&self) -> u64 {
223        self.misuse_findings
224    }
225}
226
227#[cfg_attr(
228    doc,
229    doc = "IntegrityReport\n\nFull integrity-scan output across all registered stores."
230)]
231#[derive(CandidType, Clone, Debug, Default, Deserialize)]
232pub struct IntegrityReport {
233    pub(crate) stores: Vec<IntegrityStoreSnapshot>,
234    pub(crate) totals: IntegrityTotals,
235}
236
237impl IntegrityReport {
238    /// Construct one integrity report payload.
239    #[must_use]
240    pub(crate) const fn new(stores: Vec<IntegrityStoreSnapshot>, totals: IntegrityTotals) -> Self {
241        Self { stores, totals }
242    }
243
244    /// Borrow per-store integrity snapshots.
245    #[must_use]
246    pub const fn stores(&self) -> &[IntegrityStoreSnapshot] {
247        self.stores.as_slice()
248    }
249
250    /// Borrow aggregated integrity totals.
251    #[must_use]
252    pub const fn totals(&self) -> &IntegrityTotals {
253        &self.totals
254    }
255}
256
257impl StorageReport {
258    /// Construct one storage report payload.
259    #[must_use]
260    pub(crate) const fn new(
261        storage_data: Vec<DataStoreSnapshot>,
262        storage_index: Vec<IndexStoreSnapshot>,
263        entity_storage: Vec<EntitySnapshot>,
264        corrupted_keys: u64,
265        corrupted_entries: u64,
266    ) -> Self {
267        Self {
268            storage_data,
269            storage_index,
270            entity_storage,
271            corrupted_keys,
272            corrupted_entries,
273        }
274    }
275
276    /// Borrow data-store snapshots.
277    #[must_use]
278    pub const fn storage_data(&self) -> &[DataStoreSnapshot] {
279        self.storage_data.as_slice()
280    }
281
282    /// Borrow index-store snapshots.
283    #[must_use]
284    pub const fn storage_index(&self) -> &[IndexStoreSnapshot] {
285        self.storage_index.as_slice()
286    }
287
288    /// Borrow entity-level storage snapshots.
289    #[must_use]
290    pub const fn entity_storage(&self) -> &[EntitySnapshot] {
291        self.entity_storage.as_slice()
292    }
293
294    /// Return count of corrupted decoded data keys.
295    #[must_use]
296    pub const fn corrupted_keys(&self) -> u64 {
297        self.corrupted_keys
298    }
299
300    /// Return count of corrupted index entries.
301    #[must_use]
302    pub const fn corrupted_entries(&self) -> u64 {
303        self.corrupted_entries
304    }
305}
306
307#[cfg_attr(doc, doc = "DataStoreSnapshot\n\nData-store snapshot row.")]
308#[derive(CandidType, Clone, Debug, Default, Deserialize)]
309pub struct DataStoreSnapshot {
310    pub(crate) path: String,
311    pub(crate) entries: u64,
312    pub(crate) memory_bytes: u64,
313}
314
315impl DataStoreSnapshot {
316    /// Construct one data-store snapshot row.
317    #[must_use]
318    pub(crate) const fn new(path: String, entries: u64, memory_bytes: u64) -> Self {
319        Self {
320            path,
321            entries,
322            memory_bytes,
323        }
324    }
325
326    /// Borrow store path.
327    #[must_use]
328    pub const fn path(&self) -> &str {
329        self.path.as_str()
330    }
331
332    /// Return row count.
333    #[must_use]
334    pub const fn entries(&self) -> u64 {
335        self.entries
336    }
337
338    /// Return memory usage in bytes.
339    #[must_use]
340    pub const fn memory_bytes(&self) -> u64 {
341        self.memory_bytes
342    }
343}
344
345#[cfg_attr(doc, doc = "IndexStoreSnapshot\n\nIndex-store snapshot row.")]
346#[derive(CandidType, Clone, Debug, Default, Deserialize)]
347pub struct IndexStoreSnapshot {
348    pub(crate) path: String,
349    pub(crate) entries: u64,
350    pub(crate) user_entries: u64,
351    pub(crate) system_entries: u64,
352    pub(crate) memory_bytes: u64,
353    pub(crate) state: IndexState,
354}
355
356impl IndexStoreSnapshot {
357    /// Construct one index-store snapshot row.
358    #[must_use]
359    pub(crate) const fn new(
360        path: String,
361        entries: u64,
362        user_entries: u64,
363        system_entries: u64,
364        memory_bytes: u64,
365        state: IndexState,
366    ) -> Self {
367        Self {
368            path,
369            entries,
370            user_entries,
371            system_entries,
372            memory_bytes,
373            state,
374        }
375    }
376
377    /// Borrow store path.
378    #[must_use]
379    pub const fn path(&self) -> &str {
380        self.path.as_str()
381    }
382
383    /// Return total entry count.
384    #[must_use]
385    pub const fn entries(&self) -> u64 {
386        self.entries
387    }
388
389    /// Return user-namespace entry count.
390    #[must_use]
391    pub const fn user_entries(&self) -> u64 {
392        self.user_entries
393    }
394
395    /// Return system-namespace entry count.
396    #[must_use]
397    pub const fn system_entries(&self) -> u64 {
398        self.system_entries
399    }
400
401    /// Return memory usage in bytes.
402    #[must_use]
403    pub const fn memory_bytes(&self) -> u64 {
404        self.memory_bytes
405    }
406
407    /// Return the current explicit runtime lifecycle state for this index
408    /// store snapshot.
409    #[must_use]
410    pub const fn state(&self) -> IndexState {
411        self.state
412    }
413}
414
415#[cfg_attr(doc, doc = "EntitySnapshot\n\nPer-entity storage snapshot row.")]
416#[derive(CandidType, Clone, Debug, Default, Deserialize)]
417pub struct EntitySnapshot {
418    pub(crate) store: String,
419
420    pub(crate) path: String,
421
422    pub(crate) entries: u64,
423
424    pub(crate) memory_bytes: u64,
425}
426
427impl EntitySnapshot {
428    /// Construct one entity-storage snapshot row.
429    #[must_use]
430    pub(crate) const fn new(store: String, path: String, entries: u64, memory_bytes: u64) -> Self {
431        Self {
432            store,
433            path,
434            entries,
435            memory_bytes,
436        }
437    }
438
439    /// Borrow store path.
440    #[must_use]
441    pub const fn store(&self) -> &str {
442        self.store.as_str()
443    }
444
445    /// Borrow entity path.
446    #[must_use]
447    pub const fn path(&self) -> &str {
448        self.path.as_str()
449    }
450
451    /// Return row count.
452    #[must_use]
453    pub const fn entries(&self) -> u64 {
454        self.entries
455    }
456
457    /// Return memory usage in bytes.
458    #[must_use]
459    pub const fn memory_bytes(&self) -> u64 {
460        self.memory_bytes
461    }
462}