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