1use crate::{
2 constants::WASM_PAGE_SIZE_BYTES,
3 ledger::{AllocationLedger, AllocationRecord},
4};
5use serde::Serialize;
6
7#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
17pub struct DiagnosticExport {
18 pub current_generation: u64,
20 pub records: Vec<DiagnosticRecord>,
22}
23
24impl DiagnosticExport {
25 #[must_use]
32 #[cfg(any(test, feature = "test-support"))]
33 pub fn from_ledger(ledger: &AllocationLedger) -> Self {
34 Self::from_records(ledger.current_generation, ledger.records.iter().cloned())
35 }
36
37 pub(crate) fn from_owned_ledger(ledger: AllocationLedger) -> Self {
40 Self::from_records(ledger.current_generation, ledger.records.into_iter())
41 }
42
43 fn from_records(
44 current_generation: u64,
45 records: impl Iterator<Item = AllocationRecord>,
46 ) -> Self {
47 Self {
48 current_generation,
49 records: records
50 .map(|allocation| DiagnosticRecord {
51 allocation,
52 memory_size: None,
53 })
54 .collect(),
55 }
56 }
57}
58
59#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
64pub struct DiagnosticRecord {
65 pub allocation: AllocationRecord,
67 #[serde(skip_serializing_if = "Option::is_none")]
72 pub memory_size: Option<DiagnosticMemorySize>,
73}
74
75#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Serialize)]
82pub struct DiagnosticMemorySize {
83 pub wasm_pages: u64,
85}
86
87impl DiagnosticMemorySize {
88 #[must_use]
90 pub const fn from_wasm_pages(wasm_pages: u64) -> Self {
91 Self { wasm_pages }
92 }
93
94 #[must_use]
96 pub const fn bytes(self) -> u64 {
97 self.wasm_pages.saturating_mul(WASM_PAGE_SIZE_BYTES)
98 }
99}
100
101#[cfg(test)]
102mod tests {
103 use super::*;
104 use crate::{AllocationDeclaration, MemoryManagerSlot, ledger::AllocationRecord};
105
106 #[test]
107 fn diagnostic_export_copies_ledger_records() {
108 let declaration = AllocationDeclaration::new(
109 "app.users.v1",
110 MemoryManagerSlot::new(100).expect("usable slot"),
111 )
112 .expect("declaration");
113 let ledger = AllocationLedger {
114 current_generation: 3,
115 records: vec![AllocationRecord::from_declaration(&declaration)],
116 };
117
118 let export = DiagnosticExport::from_ledger(&ledger);
119
120 assert_eq!(export.current_generation, 3);
121 assert_eq!(export.records.len(), 1);
122 assert_eq!(export.records[0].memory_size, None);
123
124 assert_eq!(export, DiagnosticExport::from_owned_ledger(ledger));
125 let wire = serde_json::to_value(&export).expect("diagnostic JSON");
126
127 assert_eq!(
128 wire,
129 serde_json::json!({
130 "current_generation": 3,
131 "records": [{"allocation": {"stable_key": "app.users.v1", "slot": 100}}]
132 })
133 );
134 }
135
136 #[test]
137 fn diagnostic_export_can_include_memory_sizes() {
138 let declaration = AllocationDeclaration::new(
139 "app.users.v1",
140 MemoryManagerSlot::new(100).expect("usable slot"),
141 )
142 .expect("declaration");
143 let ledger = AllocationLedger {
144 current_generation: 3,
145 records: vec![AllocationRecord::from_declaration(&declaration)],
146 };
147
148 let mut export = DiagnosticExport::from_ledger(&ledger);
149 export.records[0].memory_size = Some(DiagnosticMemorySize::from_wasm_pages(2));
150
151 assert_eq!(
152 export.records[0].memory_size,
153 Some(DiagnosticMemorySize { wasm_pages: 2 })
154 );
155 let wire = serde_json::to_value(&export).expect("diagnostic JSON");
156 assert_eq!(
157 wire["records"][0]["memory_size"],
158 serde_json::json!({"wasm_pages": 2})
159 );
160 }
161}