use crate::js::externals::{Extern, Function, Global, Memory, Table};
use crate::js::native::TypedFunction;
use crate::js::store::AsStoreRef;
use crate::js::WasmTypeList;
use indexmap::IndexMap;
use std::fmt;
use std::iter::{ExactSizeIterator, FromIterator};
use thiserror::Error;
#[derive(Error, Debug)]
pub enum ExportError {
#[error("Incompatible Export Type")]
IncompatibleType,
#[error("Missing export {0}")]
Missing(String),
}
#[derive(Clone, Default)]
pub struct Exports {
map: IndexMap<String, Extern>,
}
impl Exports {
pub fn new() -> Self {
Default::default()
}
pub fn with_capacity(n: usize) -> Self {
Self {
map: IndexMap::with_capacity(n),
}
}
pub fn len(&self) -> usize {
self.map.len()
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn insert<S, E>(&mut self, name: S, value: E)
where
S: Into<String>,
E: Into<Extern>,
{
self.map.insert(name.into(), value.into());
}
pub fn get<'a, T: Exportable<'a>>(&'a self, name: &str) -> Result<&'a T, ExportError> {
match self.map.get(name) {
None => Err(ExportError::Missing(name.to_string())),
Some(extern_) => T::get_self_from_extern(extern_),
}
}
pub fn get_global(&self, name: &str) -> Result<&Global, ExportError> {
self.get(name)
}
pub fn get_memory(&self, name: &str) -> Result<&Memory, ExportError> {
self.get(name)
}
pub fn get_table(&self, name: &str) -> Result<&Table, ExportError> {
self.get(name)
}
pub fn get_function(&self, name: &str) -> Result<&Function, ExportError> {
self.get(name)
}
#[deprecated(
since = "3.0.0",
note = "get_native_function() has been renamed to get_typed_function(), just like NativeFunc has been renamed to TypedFunction."
)]
pub fn get_native_function<Args, Rets>(
&self,
store: &impl AsStoreRef,
name: &str,
) -> Result<TypedFunction<Args, Rets>, ExportError>
where
Args: WasmTypeList,
Rets: WasmTypeList,
{
self.get_typed_function(store, name)
}
pub fn get_typed_function<Args, Rets>(
&self,
store: &impl AsStoreRef,
name: &str,
) -> Result<TypedFunction<Args, Rets>, ExportError>
where
Args: WasmTypeList,
Rets: WasmTypeList,
{
self.get_function(name)?
.typed(store)
.map_err(|_| ExportError::IncompatibleType)
}
pub fn get_with_generics<'a, T, Args, Rets>(
&'a self,
store: &impl AsStoreRef,
name: &str,
) -> Result<T, ExportError>
where
Args: WasmTypeList,
Rets: WasmTypeList,
T: ExportableWithGenerics<'a, Args, Rets>,
{
match self.map.get(name) {
None => Err(ExportError::Missing(name.to_string())),
Some(extern_) => T::get_self_from_extern_with_generics(store, extern_),
}
}
pub fn get_with_generics_weak<'a, T, Args, Rets>(
&'a self,
store: &impl AsStoreRef,
name: &str,
) -> Result<T, ExportError>
where
Args: WasmTypeList,
Rets: WasmTypeList,
T: ExportableWithGenerics<'a, Args, Rets>,
{
let out: T = self.get_with_generics(store, name)?;
Ok(out)
}
pub fn get_extern(&self, name: &str) -> Option<&Extern> {
self.map.get(name)
}
pub fn contains<S>(&self, name: S) -> bool
where
S: Into<String>,
{
self.map.contains_key(&name.into())
}
pub fn iter(&self) -> ExportsIterator<impl Iterator<Item = (&String, &Extern)>> {
ExportsIterator {
iter: self.map.iter(),
}
}
}
impl fmt::Debug for Exports {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_set().entries(self.iter()).finish()
}
}
pub struct ExportsIterator<'a, I>
where
I: Iterator<Item = (&'a String, &'a Extern)> + Sized,
{
iter: I,
}
impl<'a, I> Iterator for ExportsIterator<'a, I>
where
I: Iterator<Item = (&'a String, &'a Extern)> + Sized,
{
type Item = (&'a String, &'a Extern);
fn next(&mut self) -> Option<Self::Item> {
self.iter.next()
}
}
impl<'a, I> ExactSizeIterator for ExportsIterator<'a, I>
where
I: Iterator<Item = (&'a String, &'a Extern)> + ExactSizeIterator + Sized,
{
fn len(&self) -> usize {
self.iter.len()
}
}
impl<'a, I> ExportsIterator<'a, I>
where
I: Iterator<Item = (&'a String, &'a Extern)> + Sized,
{
pub fn functions(self) -> impl Iterator<Item = (&'a String, &'a Function)> + Sized {
self.iter.filter_map(|(name, export)| match export {
Extern::Function(function) => Some((name, function)),
_ => None,
})
}
pub fn memories(self) -> impl Iterator<Item = (&'a String, &'a Memory)> + Sized {
self.iter.filter_map(|(name, export)| match export {
Extern::Memory(memory) => Some((name, memory)),
_ => None,
})
}
pub fn globals(self) -> impl Iterator<Item = (&'a String, &'a Global)> + Sized {
self.iter.filter_map(|(name, export)| match export {
Extern::Global(global) => Some((name, global)),
_ => None,
})
}
pub fn tables(self) -> impl Iterator<Item = (&'a String, &'a Table)> + Sized {
self.iter.filter_map(|(name, export)| match export {
Extern::Table(table) => Some((name, table)),
_ => None,
})
}
}
impl FromIterator<(String, Extern)> for Exports {
fn from_iter<I: IntoIterator<Item = (String, Extern)>>(iter: I) -> Self {
Self {
map: IndexMap::from_iter(iter),
}
}
}
impl IntoIterator for Exports {
type IntoIter = indexmap::map::IntoIter<String, Extern>;
type Item = (String, Extern);
fn into_iter(self) -> Self::IntoIter {
self.map.clone().into_iter()
}
}
impl<'a> IntoIterator for &'a Exports {
type IntoIter = indexmap::map::Iter<'a, String, Extern>;
type Item = (&'a String, &'a Extern);
fn into_iter(self) -> Self::IntoIter {
self.map.iter()
}
}
pub trait Exportable<'a>: Sized {
fn get_self_from_extern(_extern: &'a Extern) -> Result<&'a Self, ExportError>;
}
pub trait ExportableWithGenerics<'a, Args: WasmTypeList, Rets: WasmTypeList>: Sized {
fn get_self_from_extern_with_generics(
store: &impl AsStoreRef,
_extern: &'a Extern,
) -> Result<Self, ExportError>;
}
impl<'a, T: Exportable<'a> + Clone + 'static> ExportableWithGenerics<'a, (), ()> for T {
fn get_self_from_extern_with_generics(
_store: &impl AsStoreRef,
_extern: &'a Extern,
) -> Result<Self, ExportError> {
T::get_self_from_extern(_extern).map(|i| i.clone())
}
}