1use crate::export::Export;
5use std::collections::VecDeque;
6use std::collections::{hash_map::Entry, HashMap};
7use std::{
8 borrow::{Borrow, BorrowMut},
9 ffi::c_void,
10 sync::{Arc, Mutex},
11};
12
13pub trait LikeNamespace {
16 fn get_export(&self, name: &str) -> Option<Export>;
18 fn get_exports(&self) -> Vec<(String, Export)>;
20 fn maybe_insert(&mut self, name: &str, export: Export) -> Option<()>;
22}
23
24pub trait IsExport {
26 fn to_export(&self) -> Export;
28}
29
30impl IsExport for Export {
31 fn to_export(&self) -> Export {
32 self.clone()
33 }
34}
35
36#[derive(Clone)]
58pub struct ImportObject {
59 map: Arc<Mutex<HashMap<String, Box<dyn LikeNamespace + Send>>>>,
60 pub(crate) state_creator:
61 Option<Arc<dyn Fn() -> (*mut c_void, fn(*mut c_void)) + Send + Sync + 'static>>,
62 pub allow_missing_functions: bool,
65}
66
67impl ImportObject {
68 pub fn new() -> Self {
70 Self {
71 map: Arc::new(Mutex::new(HashMap::new())),
72 state_creator: None,
73 allow_missing_functions: false,
74 }
75 }
76
77 pub fn new_with_data<F>(state_creator: F) -> Self
79 where
80 F: Fn() -> (*mut c_void, fn(*mut c_void)) + 'static + Send + Sync,
81 {
82 Self {
83 map: Arc::new(Mutex::new(HashMap::new())),
84 state_creator: Some(Arc::new(state_creator)),
85 allow_missing_functions: false,
86 }
87 }
88
89 pub(crate) fn call_state_creator(&self) -> Option<(*mut c_void, fn(*mut c_void))> {
90 self.state_creator.as_ref().map(|state_gen| state_gen())
91 }
92
93 pub fn register<S, N>(&mut self, name: S, namespace: N) -> Option<Box<dyn LikeNamespace>>
108 where
109 S: Into<String>,
110 N: LikeNamespace + Send + 'static,
111 {
112 let mut guard = self.map.lock().unwrap();
113 let map = guard.borrow_mut();
114
115 match map.entry(name.into()) {
116 Entry::Vacant(empty) => {
117 empty.insert(Box::new(namespace));
118 None
119 }
120 Entry::Occupied(mut occupied) => Some(occupied.insert(Box::new(namespace))),
121 }
122 }
123
124 pub fn with_namespace<Func, InnerRet>(&self, namespace: &str, f: Func) -> Option<InnerRet>
127 where
128 Func: FnOnce(&(dyn LikeNamespace + Send)) -> InnerRet,
129 InnerRet: Sized,
130 {
131 let guard = self.map.lock().unwrap();
132 let map_ref = guard.borrow();
133 if map_ref.contains_key(namespace) {
134 Some(f(map_ref[namespace].as_ref()))
135 } else {
136 None
137 }
138 }
139
140 pub fn maybe_with_namespace<Func, InnerRet>(&self, namespace: &str, f: Func) -> Option<InnerRet>
150 where
151 Func: FnOnce(&(dyn LikeNamespace + Send)) -> Option<InnerRet>,
152 InnerRet: Sized,
153 {
154 let guard = self.map.lock().unwrap();
155 let map_ref = guard.borrow();
156 map_ref
157 .get(namespace)
158 .map(|ns| ns.as_ref())
159 .and_then(|ns| f(ns))
160 }
161
162 fn get_objects(&self) -> VecDeque<(String, String, Export)> {
163 let mut out = VecDeque::new();
164 let guard = self.map.lock().unwrap();
165 let map = guard.borrow();
166 for (name, ns) in map.iter() {
167 for (id, exp) in ns.get_exports() {
168 out.push_back((name.clone(), id, exp));
169 }
170 }
171 out
172 }
173
174 pub fn contains_namespace(&self, name: &str) -> bool {
176 self.map.lock().unwrap().borrow().contains_key(name)
177 }
178}
179
180pub struct ImportObjectIterator {
182 elements: VecDeque<(String, String, Export)>,
183}
184
185impl Iterator for ImportObjectIterator {
186 type Item = (String, String, Export);
187
188 fn next(&mut self) -> Option<Self::Item> {
189 self.elements.pop_front()
190 }
191
192 fn size_hint(&self) -> (usize, Option<usize>) {
193 let len = self.elements.len();
194
195 (len, Some(len))
196 }
197}
198
199impl IntoIterator for ImportObject {
200 type IntoIter = ImportObjectIterator;
201 type Item = (String, String, Export);
202
203 fn into_iter(self) -> Self::IntoIter {
204 ImportObjectIterator {
205 elements: self.get_objects(),
206 }
207 }
208}
209
210impl Extend<(String, String, Export)> for ImportObject {
211 fn extend<T: IntoIterator<Item = (String, String, Export)>>(&mut self, iter: T) {
212 let mut guard = self.map.lock().unwrap();
213 let map = guard.borrow_mut();
214 for (ns, id, exp) in iter.into_iter() {
215 if let Some(like_ns) = map.get_mut(&ns) {
216 like_ns.maybe_insert(&id, exp);
217 } else {
218 let mut new_ns = Namespace::new();
219 new_ns.insert(id, exp);
220 map.insert(ns, Box::new(new_ns));
221 }
222 }
223 }
224}
225
226pub struct Namespace {
228 map: HashMap<String, Box<dyn IsExport + Send>>,
229}
230
231impl Namespace {
232 pub fn new() -> Self {
234 Self {
235 map: HashMap::new(),
236 }
237 }
238
239 pub fn insert<S, E>(&mut self, name: S, export: E) -> Option<Box<dyn IsExport + Send>>
241 where
242 S: Into<String>,
243 E: IsExport + Send + 'static,
244 {
245 self.map.insert(name.into(), Box::new(export))
246 }
247
248 pub fn contains_key<S>(&mut self, key: S) -> bool
250 where
251 S: Into<String>,
252 {
253 self.map.contains_key(&key.into())
254 }
255}
256
257impl LikeNamespace for Namespace {
258 fn get_export(&self, name: &str) -> Option<Export> {
259 self.map.get(name).map(|is_export| is_export.to_export())
260 }
261
262 fn get_exports(&self) -> Vec<(String, Export)> {
263 self.map
264 .iter()
265 .map(|(k, v)| (k.clone(), v.to_export()))
266 .collect()
267 }
268
269 fn maybe_insert(&mut self, name: &str, export: Export) -> Option<()> {
270 self.map.insert(name.to_owned(), Box::new(export));
271 Some(())
272 }
273}
274
275#[cfg(test)]
276mod test {
277 use crate::export::Export;
278 use crate::global::Global;
279 use crate::types::Value;
280
281 #[test]
282 fn extending_works() {
283 let mut imports1 = imports! {
284 "dog" => {
285 "happy" => Global::new(Value::I32(0)),
286 },
287 };
288
289 let imports2 = imports! {
290 "dog" => {
291 "small" => Global::new(Value::I32(2)),
292 },
293 "cat" => {
294 "small" => Global::new(Value::I32(3)),
295 },
296 };
297
298 imports1.extend(imports2);
299
300 let small_cat_export =
301 imports1.maybe_with_namespace("cat", |cat_ns| cat_ns.get_export("small"));
302 assert!(small_cat_export.is_some());
303
304 let entries = imports1.maybe_with_namespace("dog", |dog_ns| {
305 Some((dog_ns.get_export("happy")?, dog_ns.get_export("small")?))
306 });
307 assert!(entries.is_some());
308 }
309
310 #[test]
311 fn extending_conflict_overwrites() {
312 let mut imports1 = imports! {
313 "dog" => {
314 "happy" => Global::new(Value::I32(0)),
315 },
316 };
317
318 let imports2 = imports! {
319 "dog" => {
320 "happy" => Global::new(Value::I32(4)),
321 },
322 };
323
324 imports1.extend(imports2);
325 let happy_dog_entry = imports1
326 .maybe_with_namespace("dog", |dog_ns| dog_ns.get_export("happy"))
327 .unwrap();
328
329 assert!(if let Export::Global(happy_dog_global) = happy_dog_entry {
330 happy_dog_global.get() == Value::I32(4)
331 } else {
332 false
333 });
334 }
335}