1use crate::{
2 ledger::{AllocationLedger, AllocationRecord, GenerationRecord},
3 physical::CommitStoreDiagnostic,
4 slot::AllocationSlotDescriptor,
5};
6use serde::{Deserialize, Serialize};
7
8#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
13pub struct DiagnosticExport {
14 pub current_generation: u64,
16 pub ledger_anchor: AllocationSlotDescriptor,
18 pub records: Vec<DiagnosticRecord>,
20 pub generations: Vec<DiagnosticGeneration>,
22 pub commit_recovery: Option<CommitStoreDiagnostic>,
24}
25
26impl DiagnosticExport {
27 #[must_use]
29 pub fn from_ledger(ledger: &AllocationLedger, ledger_anchor: AllocationSlotDescriptor) -> Self {
30 Self::from_ledger_with_commit_recovery(ledger, ledger_anchor, None)
31 }
32
33 #[must_use]
35 pub fn from_ledger_with_commit_recovery(
36 ledger: &AllocationLedger,
37 ledger_anchor: AllocationSlotDescriptor,
38 commit_recovery: Option<CommitStoreDiagnostic>,
39 ) -> Self {
40 Self {
41 current_generation: ledger.current_generation,
42 ledger_anchor,
43 records: ledger
44 .allocation_history()
45 .records()
46 .iter()
47 .cloned()
48 .map(|allocation| DiagnosticRecord { allocation })
49 .collect(),
50 generations: ledger
51 .allocation_history()
52 .generations()
53 .iter()
54 .cloned()
55 .map(|generation| DiagnosticGeneration { generation })
56 .collect(),
57 commit_recovery,
58 }
59 }
60}
61
62#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
67pub struct DiagnosticRecord {
68 pub allocation: AllocationRecord,
70}
71
72#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
77pub struct DiagnosticGeneration {
78 pub generation: GenerationRecord,
80}
81
82#[cfg(test)]
83mod tests {
84 use super::*;
85 use crate::{
86 declaration::AllocationDeclaration,
87 ledger::{AllocationHistory, AllocationRecord, AllocationState},
88 physical::{CommitRecoveryError, CommitSlotDiagnostic, CommitStoreDiagnostic},
89 schema::SchemaMetadata,
90 };
91
92 #[test]
93 fn diagnostic_export_copies_ledger_records() {
94 let declaration = AllocationDeclaration::new(
95 "app.users.v1",
96 AllocationSlotDescriptor::memory_manager(100).expect("usable slot"),
97 None,
98 SchemaMetadata::default(),
99 )
100 .expect("declaration");
101 let ledger = AllocationLedger {
102 current_generation: 3,
103 allocation_history: AllocationHistory::from_parts(
104 vec![AllocationRecord::from_declaration(
105 3,
106 declaration,
107 AllocationState::Active,
108 )],
109 vec![GenerationRecord {
110 generation: 3,
111 parent_generation: 2,
112 runtime_fingerprint: Some("wasm:abc123".to_string()),
113 declaration_count: 1,
114 committed_at: None,
115 }],
116 ),
117 };
118
119 let export = DiagnosticExport::from_ledger(
120 &ledger,
121 AllocationSlotDescriptor::memory_manager(0).expect("usable slot"),
122 );
123
124 assert_eq!(export.current_generation, 3);
125 assert_eq!(export.records.len(), 1);
126 assert_eq!(export.generations.len(), 1);
127 assert_eq!(
128 export.ledger_anchor,
129 AllocationSlotDescriptor::memory_manager(0).expect("usable slot")
130 );
131 assert_eq!(export.commit_recovery, None);
132 }
133
134 #[test]
135 fn diagnostic_export_can_include_commit_recovery_state() {
136 let ledger = AllocationLedger {
137 current_generation: 3,
138 allocation_history: AllocationHistory::default(),
139 };
140 let commit_recovery = CommitStoreDiagnostic {
141 slot0: CommitSlotDiagnostic {
142 present: true,
143 generation: Some(3),
144 valid: true,
145 },
146 slot1: CommitSlotDiagnostic {
147 present: false,
148 generation: None,
149 valid: false,
150 },
151 authoritative_generation: Some(3),
152 recovery_error: None,
153 };
154
155 let export = DiagnosticExport::from_ledger_with_commit_recovery(
156 &ledger,
157 AllocationSlotDescriptor::memory_manager(0).expect("usable slot"),
158 Some(commit_recovery),
159 );
160
161 assert_eq!(export.commit_recovery, Some(commit_recovery));
162 }
163
164 #[test]
165 fn diagnostic_export_can_report_recovery_failure() {
166 let ledger = AllocationLedger {
167 current_generation: 0,
168 allocation_history: AllocationHistory::default(),
169 };
170 let commit_recovery = CommitStoreDiagnostic {
171 slot0: CommitSlotDiagnostic {
172 present: false,
173 generation: None,
174 valid: false,
175 },
176 slot1: CommitSlotDiagnostic {
177 present: false,
178 generation: None,
179 valid: false,
180 },
181 authoritative_generation: None,
182 recovery_error: Some(CommitRecoveryError::NoValidGeneration),
183 };
184
185 let export = DiagnosticExport::from_ledger_with_commit_recovery(
186 &ledger,
187 AllocationSlotDescriptor::memory_manager(0).expect("usable slot"),
188 Some(commit_recovery),
189 );
190
191 assert_eq!(
192 export
193 .commit_recovery
194 .expect("commit recovery")
195 .recovery_error,
196 Some(CommitRecoveryError::NoValidGeneration)
197 );
198 }
199}