1use crate::{
2 cdk::structures::{
3 DefaultMemoryImpl,
4 memory::{MemoryId, VirtualMemory},
5 },
6 manager::MEMORY_MANAGER,
7 registry::{MemoryRange, MemoryRegistry, MemoryRegistryError},
8 runtime::{MemoryRuntimeApi, registry::MemoryRegistryInitSummary},
9};
10
11pub struct MemoryApi;
16
17#[derive(Clone, Debug, Eq, PartialEq)]
22pub struct MemoryInspection {
23 pub id: u8,
24 pub owner: String,
25 pub range: MemoryRange,
26 pub label: Option<String>,
27}
28
29#[derive(Clone, Debug, Eq, PartialEq)]
34pub struct RegisteredMemory {
35 pub id: u8,
36 pub owner: String,
37 pub range: MemoryRange,
38 pub label: String,
39}
40
41impl MemoryApi {
42 pub fn bootstrap_registry(
44 crate_name: &'static str,
45 start: u8,
46 end: u8,
47 ) -> Result<MemoryRegistryInitSummary, MemoryRegistryError> {
48 MemoryRuntimeApi::bootstrap_registry(crate_name, start, end)
49 }
50
51 pub fn register_memory(
55 id: u8,
56 crate_name: &str,
57 label: &str,
58 ) -> Result<VirtualMemory<DefaultMemoryImpl>, MemoryRegistryError> {
59 if let Some(entry) = MemoryRegistry::get(id)
60 && entry.crate_name == crate_name
61 && entry.label == label
62 {
63 return Ok(open_memory(id));
64 }
65
66 MemoryRegistry::register(id, crate_name, label)?;
67 Ok(open_memory(id))
68 }
69
70 #[must_use]
72 pub fn inspect_memory(id: u8) -> Option<MemoryInspection> {
73 let range = MemoryRegistry::export_range_entries()
74 .into_iter()
75 .find(|entry| entry.range.contains(id))?;
76 let label = MemoryRegistry::get(id).map(|entry| entry.label);
77
78 Some(MemoryInspection {
79 id,
80 owner: range.owner,
81 range: range.range,
82 label,
83 })
84 }
85
86 #[must_use]
88 pub fn registered_memories() -> Vec<RegisteredMemory> {
89 MemoryRegistry::export_ids_by_range()
90 .into_iter()
91 .flat_map(|snapshot| {
92 snapshot
93 .entries
94 .into_iter()
95 .map(move |(id, entry)| RegisteredMemory {
96 id,
97 owner: snapshot.owner.clone(),
98 range: snapshot.range,
99 label: entry.label,
100 })
101 })
102 .collect()
103 }
104
105 #[must_use]
107 pub fn registered_memories_for_owner(owner: &str) -> Vec<RegisteredMemory> {
108 Self::registered_memories()
109 .into_iter()
110 .filter(|entry| entry.owner == owner)
111 .collect()
112 }
113
114 #[must_use]
116 pub fn find_registered_memory(owner: &str, label: &str) -> Option<RegisteredMemory> {
117 Self::registered_memories()
118 .into_iter()
119 .find(|entry| entry.owner == owner && entry.label == label)
120 }
121}
122
123fn open_memory(id: u8) -> VirtualMemory<DefaultMemoryImpl> {
125 MEMORY_MANAGER.with_borrow_mut(|mgr| mgr.get(MemoryId::new(id)))
126}
127
128#[cfg(test)]
133mod tests {
134 use super::*;
135 use crate::registry::{MemoryRegistryError, reset_for_tests};
136
137 #[test]
138 fn register_memory_returns_opened_memory_for_reserved_slot() {
139 reset_for_tests();
140 let _ = MemoryApi::bootstrap_registry("crate_a", 1, 3).expect("bootstrap registry");
141
142 let _memory = MemoryApi::register_memory(2, "crate_a", "slot").expect("register memory");
143 }
144
145 #[test]
146 fn register_memory_is_idempotent_for_same_entry() {
147 reset_for_tests();
148 let _ = MemoryApi::bootstrap_registry("crate_a", 1, 3).expect("bootstrap registry");
149 let _ = MemoryApi::register_memory(2, "crate_a", "slot").expect("first register succeeds");
150
151 let _ = MemoryApi::register_memory(2, "crate_a", "slot").expect("second register succeeds");
152 }
153
154 #[test]
155 fn register_memory_rejects_unreserved_id() {
156 reset_for_tests();
157
158 let Err(err) = MemoryApi::register_memory(9, "crate_a", "slot") else {
159 panic!("unreserved slot must fail")
160 };
161 assert!(matches!(err, MemoryRegistryError::NoReservedRange { .. }));
162 }
163
164 #[test]
165 fn register_memory_preserves_duplicate_id_error_for_conflicts() {
166 reset_for_tests();
167 let _ = MemoryApi::bootstrap_registry("crate_a", 1, 3).expect("bootstrap registry");
168 MemoryApi::register_memory(2, "crate_a", "slot").expect("first register succeeds");
169
170 let Err(err) = MemoryApi::register_memory(2, "crate_a", "other") else {
171 panic!("conflicting duplicate register must fail")
172 };
173 assert!(matches!(err, MemoryRegistryError::DuplicateId(2)));
174 }
175
176 #[test]
177 fn inspect_memory_returns_reserved_owner_without_label() {
178 reset_for_tests();
179 let _ = MemoryApi::bootstrap_registry("crate_a", 1, 3).expect("bootstrap registry");
180
181 let inspection = MemoryApi::inspect_memory(2).expect("reserved slot should inspect");
182 assert_eq!(inspection.owner, "crate_a");
183 assert_eq!(inspection.range, MemoryRange { start: 1, end: 3 });
184 assert_eq!(inspection.label, None);
185 }
186
187 #[test]
188 fn inspect_memory_returns_registered_label() {
189 reset_for_tests();
190 let _ = MemoryApi::bootstrap_registry("crate_a", 1, 3).expect("bootstrap registry");
191 let _ = MemoryApi::register_memory(2, "crate_a", "slot").expect("register memory");
192
193 let inspection = MemoryApi::inspect_memory(2).expect("registered slot should inspect");
194 assert_eq!(inspection.owner, "crate_a");
195 assert_eq!(inspection.range, MemoryRange { start: 1, end: 3 });
196 assert_eq!(inspection.label.as_deref(), Some("slot"));
197 }
198
199 #[test]
200 fn inspect_memory_returns_none_for_unowned_id() {
201 reset_for_tests();
202 assert_eq!(MemoryApi::inspect_memory(9), None);
203 }
204
205 #[test]
206 fn registered_memories_lists_registered_slots_with_owner_context() {
207 reset_for_tests();
208 let _ = MemoryApi::bootstrap_registry("crate_a", 1, 3).expect("bootstrap registry");
209 let _ = MemoryApi::bootstrap_registry("crate_b", 10, 12).expect("bootstrap registry");
210 let _ = MemoryApi::register_memory(2, "crate_a", "slot_a").expect("register memory");
211 let _ = MemoryApi::register_memory(11, "crate_b", "slot_b").expect("register memory");
212
213 let registrations = MemoryApi::registered_memories();
214 assert_eq!(registrations.len(), 2);
215 assert!(registrations.contains(&RegisteredMemory {
216 id: 2,
217 owner: "crate_a".to_string(),
218 range: MemoryRange { start: 1, end: 3 },
219 label: "slot_a".to_string(),
220 }));
221 assert!(registrations.contains(&RegisteredMemory {
222 id: 11,
223 owner: "crate_b".to_string(),
224 range: MemoryRange { start: 10, end: 12 },
225 label: "slot_b".to_string(),
226 }));
227 }
228
229 #[test]
230 fn registered_memories_for_owner_filters_to_owner() {
231 reset_for_tests();
232 let _ = MemoryApi::bootstrap_registry("crate_a", 1, 3).expect("bootstrap registry");
233 let _ = MemoryApi::bootstrap_registry("crate_b", 10, 12).expect("bootstrap registry");
234 let _ = MemoryApi::register_memory(2, "crate_a", "slot_a").expect("register memory");
235 let _ = MemoryApi::register_memory(11, "crate_b", "slot_b").expect("register memory");
236
237 let registrations = MemoryApi::registered_memories_for_owner("crate_a");
238 assert_eq!(
239 registrations,
240 vec![RegisteredMemory {
241 id: 2,
242 owner: "crate_a".to_string(),
243 range: MemoryRange { start: 1, end: 3 },
244 label: "slot_a".to_string(),
245 }]
246 );
247 }
248
249 #[test]
250 fn find_registered_memory_returns_match_for_owner_and_label() {
251 reset_for_tests();
252 let _ = MemoryApi::bootstrap_registry("crate_a", 1, 3).expect("bootstrap registry");
253 let _ = MemoryApi::register_memory(2, "crate_a", "slot_a").expect("register memory");
254
255 let registration =
256 MemoryApi::find_registered_memory("crate_a", "slot_a").expect("slot should exist");
257 assert_eq!(
258 registration,
259 RegisteredMemory {
260 id: 2,
261 owner: "crate_a".to_string(),
262 range: MemoryRange { start: 1, end: 3 },
263 label: "slot_a".to_string(),
264 }
265 );
266 }
267
268 #[test]
269 fn find_registered_memory_returns_none_when_missing() {
270 reset_for_tests();
271 let _ = MemoryApi::bootstrap_registry("crate_a", 1, 3).expect("bootstrap registry");
272 assert_eq!(MemoryApi::find_registered_memory("crate_a", "slot_a"), None);
273 }
274}