1use crate::store::AsStoreRef;
2use crate::{Extern, Function, Global, Memory, Table, TypedFunction, WasmTypeList};
3use indexmap::IndexMap;
4use std::fmt;
5use std::iter::{ExactSizeIterator, FromIterator};
6use thiserror::Error;
7
8#[derive(Error, Debug, Clone)]
46pub enum ExportError {
47 #[error("Incompatible Export Type")]
50 IncompatibleType,
51 #[error("Missing export {0}")]
53 Missing(String),
54}
55
56#[derive(Clone, Default, PartialEq, Eq)]
61#[cfg_attr(feature = "artifact-size", derive(loupe::MemoryUsage))]
62pub struct Exports {
63 map: IndexMap<String, Extern>,
64}
65
66impl Exports {
67 pub fn new() -> Self {
69 Default::default()
70 }
71
72 pub fn with_capacity(n: usize) -> Self {
74 Self {
75 map: IndexMap::with_capacity(n),
76 }
77 }
78
79 pub fn len(&self) -> usize {
81 self.map.len()
82 }
83
84 pub fn is_empty(&self) -> bool {
86 self.len() == 0
87 }
88
89 pub fn insert<S, E>(&mut self, name: S, value: E)
91 where
92 S: Into<String>,
93 E: Into<Extern>,
94 {
95 self.map.insert(name.into(), value.into());
96 }
97
98 pub fn get<'a, T: Exportable<'a>>(&'a self, name: &str) -> Result<&'a T, ExportError> {
110 match self.map.get(name) {
111 None => Err(ExportError::Missing(name.to_string())),
112 Some(extern_) => T::get_self_from_extern(extern_),
113 }
114 }
115
116 pub fn get_global(&self, name: &str) -> Result<&Global, ExportError> {
118 self.get(name)
119 }
120
121 pub fn get_memory(&self, name: &str) -> Result<&Memory, ExportError> {
123 self.get(name)
124 }
125
126 pub fn get_table(&self, name: &str) -> Result<&Table, ExportError> {
128 self.get(name)
129 }
130
131 pub fn get_function(&self, name: &str) -> Result<&Function, ExportError> {
133 self.get(name)
134 }
135
136 pub fn get_typed_function<Args, Rets>(
138 &self,
139 store: &impl AsStoreRef,
140 name: &str,
141 ) -> Result<TypedFunction<Args, Rets>, ExportError>
142 where
143 Args: WasmTypeList,
144 Rets: WasmTypeList,
145 {
146 self.get_function(name)?
147 .typed(store)
148 .map_err(|_| ExportError::IncompatibleType)
149 }
150
151 pub fn get_with_generics<'a, T, Args, Rets>(&'a self, name: &str) -> Result<T, ExportError>
153 where
154 Args: WasmTypeList,
155 Rets: WasmTypeList,
156 T: ExportableWithGenerics<'a, Args, Rets>,
157 {
158 match self.map.get(name) {
159 None => Err(ExportError::Missing(name.to_string())),
160 Some(extern_) => T::get_self_from_extern_with_generics(extern_),
161 }
162 }
163
164 pub fn get_extern(&self, name: &str) -> Option<&Extern> {
166 self.map.get(name)
167 }
168
169 pub fn contains<S>(&self, name: S) -> bool
171 where
172 S: Into<String>,
173 {
174 self.map.contains_key(&name.into())
175 }
176
177 pub fn iter(&self) -> ExportsIterator<impl Iterator<Item = (&String, &Extern)>> {
179 ExportsIterator {
180 iter: self.map.iter(),
181 }
182 }
183}
184
185impl fmt::Debug for Exports {
186 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
187 f.debug_set().entries(self.iter()).finish()
188 }
189}
190
191pub struct ExportsIterator<'a, I>
193where
194 I: Iterator<Item = (&'a String, &'a Extern)> + Sized,
195{
196 iter: I,
197}
198
199impl<'a, I> Iterator for ExportsIterator<'a, I>
200where
201 I: Iterator<Item = (&'a String, &'a Extern)> + Sized,
202{
203 type Item = (&'a String, &'a Extern);
204
205 fn next(&mut self) -> Option<Self::Item> {
206 self.iter.next()
207 }
208}
209
210impl<'a, I> ExactSizeIterator for ExportsIterator<'a, I>
211where
212 I: Iterator<Item = (&'a String, &'a Extern)> + ExactSizeIterator + Sized,
213{
214 fn len(&self) -> usize {
215 self.iter.len()
216 }
217}
218
219impl<'a, I> ExportsIterator<'a, I>
220where
221 I: Iterator<Item = (&'a String, &'a Extern)> + Sized,
222{
223 pub fn functions(self) -> impl Iterator<Item = (&'a String, &'a Function)> + Sized {
225 self.iter.filter_map(|(name, export)| match export {
226 Extern::Function(function) => Some((name, function)),
227 _ => None,
228 })
229 }
230
231 pub fn memories(self) -> impl Iterator<Item = (&'a String, &'a Memory)> + Sized {
233 self.iter.filter_map(|(name, export)| match export {
234 Extern::Memory(memory) => Some((name, memory)),
235 _ => None,
236 })
237 }
238
239 pub fn globals(self) -> impl Iterator<Item = (&'a String, &'a Global)> + Sized {
241 self.iter.filter_map(|(name, export)| match export {
242 Extern::Global(global) => Some((name, global)),
243 _ => None,
244 })
245 }
246
247 pub fn tables(self) -> impl Iterator<Item = (&'a String, &'a Table)> + Sized {
249 self.iter.filter_map(|(name, export)| match export {
250 Extern::Table(table) => Some((name, table)),
251 _ => None,
252 })
253 }
254}
255
256impl FromIterator<(String, Extern)> for Exports {
257 fn from_iter<I: IntoIterator<Item = (String, Extern)>>(iter: I) -> Self {
258 Self {
259 map: IndexMap::from_iter(iter),
260 }
261 }
262}
263
264impl IntoIterator for Exports {
265 type IntoIter = indexmap::map::IntoIter<String, Extern>;
266 type Item = (String, Extern);
267
268 fn into_iter(self) -> Self::IntoIter {
269 self.map.into_iter()
270 }
271}
272
273impl<'a> IntoIterator for &'a Exports {
274 type IntoIter = indexmap::map::Iter<'a, String, Extern>;
275 type Item = (&'a String, &'a Extern);
276
277 fn into_iter(self) -> Self::IntoIter {
278 self.map.iter()
279 }
280}
281
282pub trait Exportable<'a>: Sized {
286 fn get_self_from_extern(_extern: &'a Extern) -> Result<&'a Self, ExportError>;
291}
292
293pub trait ExportableWithGenerics<'a, Args: WasmTypeList, Rets: WasmTypeList>: Sized {
297 fn get_self_from_extern_with_generics(_extern: &'a Extern) -> Result<Self, ExportError>;
299}
300
301impl<'a, T: Exportable<'a> + Clone + 'static> ExportableWithGenerics<'a, (), ()> for T {
304 fn get_self_from_extern_with_generics(_extern: &'a Extern) -> Result<Self, ExportError> {
305 T::get_self_from_extern(_extern).map(|i| i.clone())
306 }
307}