pub use wasmparser;
use cranelift_entity::entity_impl;
use serde::{Deserialize, Serialize};
use std::convert::{TryFrom, TryInto};
use std::fmt;
mod error;
pub use error::*;
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum WasmType {
    I32,
    I64,
    F32,
    F64,
    V128,
    FuncRef,
    ExternRef,
}
impl TryFrom<wasmparser::ValType> for WasmType {
    type Error = WasmError;
    fn try_from(ty: wasmparser::ValType) -> Result<Self, Self::Error> {
        use wasmparser::ValType::*;
        match ty {
            I32 => Ok(WasmType::I32),
            I64 => Ok(WasmType::I64),
            F32 => Ok(WasmType::F32),
            F64 => Ok(WasmType::F64),
            V128 => Ok(WasmType::V128),
            Ref(r) => r.try_into(),
        }
    }
}
impl TryFrom<wasmparser::RefType> for WasmType {
    type Error = WasmError;
    fn try_from(ty: wasmparser::RefType) -> Result<Self, Self::Error> {
        match ty {
            wasmparser::RefType::FUNCREF => Ok(WasmType::FuncRef),
            wasmparser::RefType::EXTERNREF => Ok(WasmType::ExternRef),
            _ => Err(WasmError::Unsupported(
                "function references proposal".to_string(),
            )),
        }
    }
}
impl TryFrom<wasmparser::HeapType> for WasmType {
    type Error = WasmError;
    fn try_from(ty: wasmparser::HeapType) -> Result<Self, Self::Error> {
        match ty {
            wasmparser::HeapType::Func => Ok(WasmType::FuncRef),
            wasmparser::HeapType::Extern => Ok(WasmType::ExternRef),
            _ => Err(WasmError::Unsupported(
                "function references proposal".to_string(),
            )),
        }
    }
}
impl From<WasmType> for wasmparser::ValType {
    fn from(ty: WasmType) -> wasmparser::ValType {
        match ty {
            WasmType::I32 => wasmparser::ValType::I32,
            WasmType::I64 => wasmparser::ValType::I64,
            WasmType::F32 => wasmparser::ValType::F32,
            WasmType::F64 => wasmparser::ValType::F64,
            WasmType::V128 => wasmparser::ValType::V128,
            WasmType::FuncRef => wasmparser::ValType::FUNCREF,
            WasmType::ExternRef => wasmparser::ValType::EXTERNREF,
        }
    }
}
impl fmt::Display for WasmType {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            WasmType::I32 => write!(f, "i32"),
            WasmType::I64 => write!(f, "i64"),
            WasmType::F32 => write!(f, "f32"),
            WasmType::F64 => write!(f, "f64"),
            WasmType::V128 => write!(f, "v128"),
            WasmType::ExternRef => write!(f, "externref"),
            WasmType::FuncRef => write!(f, "funcref"),
        }
    }
}
#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)]
pub struct WasmFuncType {
    params: Box<[WasmType]>,
    externref_params_count: usize,
    returns: Box<[WasmType]>,
    externref_returns_count: usize,
}
impl WasmFuncType {
    #[inline]
    pub fn new(params: Box<[WasmType]>, returns: Box<[WasmType]>) -> Self {
        let externref_params_count = params.iter().filter(|p| **p == WasmType::ExternRef).count();
        let externref_returns_count = returns
            .iter()
            .filter(|r| **r == WasmType::ExternRef)
            .count();
        WasmFuncType {
            params,
            externref_params_count,
            returns,
            externref_returns_count,
        }
    }
    #[inline]
    pub fn params(&self) -> &[WasmType] {
        &self.params
    }
    #[inline]
    pub fn externref_params_count(&self) -> usize {
        self.externref_params_count
    }
    #[inline]
    pub fn returns(&self) -> &[WasmType] {
        &self.returns
    }
    #[inline]
    pub fn externref_returns_count(&self) -> usize {
        self.externref_returns_count
    }
}
impl TryFrom<wasmparser::FuncType> for WasmFuncType {
    type Error = WasmError;
    fn try_from(ty: wasmparser::FuncType) -> Result<Self, Self::Error> {
        let params = ty
            .params()
            .iter()
            .copied()
            .map(WasmType::try_from)
            .collect::<Result<_, Self::Error>>()?;
        let returns = ty
            .results()
            .iter()
            .copied()
            .map(WasmType::try_from)
            .collect::<Result<_, Self::Error>>()?;
        Ok(Self::new(params, returns))
    }
}
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)]
pub struct FuncIndex(u32);
entity_impl!(FuncIndex);
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)]
pub struct DefinedFuncIndex(u32);
entity_impl!(DefinedFuncIndex);
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)]
pub struct DefinedTableIndex(u32);
entity_impl!(DefinedTableIndex);
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)]
pub struct DefinedMemoryIndex(u32);
entity_impl!(DefinedMemoryIndex);
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)]
pub struct OwnedMemoryIndex(u32);
entity_impl!(OwnedMemoryIndex);
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)]
pub struct DefinedGlobalIndex(u32);
entity_impl!(DefinedGlobalIndex);
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)]
pub struct TableIndex(u32);
entity_impl!(TableIndex);
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)]
pub struct GlobalIndex(u32);
entity_impl!(GlobalIndex);
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)]
pub struct MemoryIndex(u32);
entity_impl!(MemoryIndex);
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)]
pub struct SignatureIndex(u32);
entity_impl!(SignatureIndex);
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)]
pub struct DataIndex(u32);
entity_impl!(DataIndex);
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)]
pub struct ElemIndex(u32);
entity_impl!(ElemIndex);
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)]
pub struct TypeIndex(u32);
entity_impl!(TypeIndex);
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)]
pub struct TagIndex(u32);
entity_impl!(TagIndex);
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)]
pub enum EntityIndex {
    Function(FuncIndex),
    Table(TableIndex),
    Memory(MemoryIndex),
    Global(GlobalIndex),
}
impl From<FuncIndex> for EntityIndex {
    fn from(idx: FuncIndex) -> EntityIndex {
        EntityIndex::Function(idx)
    }
}
impl From<TableIndex> for EntityIndex {
    fn from(idx: TableIndex) -> EntityIndex {
        EntityIndex::Table(idx)
    }
}
impl From<MemoryIndex> for EntityIndex {
    fn from(idx: MemoryIndex) -> EntityIndex {
        EntityIndex::Memory(idx)
    }
}
impl From<GlobalIndex> for EntityIndex {
    fn from(idx: GlobalIndex) -> EntityIndex {
        EntityIndex::Global(idx)
    }
}
#[allow(missing_docs)]
#[derive(Clone, Debug, Serialize, Deserialize)]
pub enum EntityType {
    Global(Global),
    Memory(Memory),
    Tag(Tag),
    Table(Table),
    Function(SignatureIndex),
}
#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq, Serialize, Deserialize)]
pub struct Global {
    pub wasm_ty: crate::WasmType,
    pub mutability: bool,
    pub initializer: GlobalInit,
}
#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq, Serialize, Deserialize)]
pub enum GlobalInit {
    I32Const(i32),
    I64Const(i64),
    F32Const(u32),
    F64Const(u64),
    V128Const(u128),
    GetGlobal(GlobalIndex),
    RefNullConst,
    RefFunc(FuncIndex),
    Import,
}
impl Global {
    pub fn new(ty: wasmparser::GlobalType, initializer: GlobalInit) -> WasmResult<Global> {
        Ok(Global {
            wasm_ty: ty.content_type.try_into()?,
            mutability: ty.mutable,
            initializer,
        })
    }
}
#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq, Serialize, Deserialize)]
pub struct Table {
    pub wasm_ty: WasmType,
    pub minimum: u32,
    pub maximum: Option<u32>,
}
impl TryFrom<wasmparser::TableType> for Table {
    type Error = WasmError;
    fn try_from(ty: wasmparser::TableType) -> WasmResult<Table> {
        Ok(Table {
            wasm_ty: ty.element_type.try_into()?,
            minimum: ty.initial,
            maximum: ty.maximum,
        })
    }
}
#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq, Serialize, Deserialize)]
pub struct Memory {
    pub minimum: u64,
    pub maximum: Option<u64>,
    pub shared: bool,
    pub memory64: bool,
}
impl From<wasmparser::MemoryType> for Memory {
    fn from(ty: wasmparser::MemoryType) -> Memory {
        Memory {
            minimum: ty.initial,
            maximum: ty.maximum,
            shared: ty.shared,
            memory64: ty.memory64,
        }
    }
}
#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq, Serialize, Deserialize)]
pub struct Tag {
    pub ty: TypeIndex,
}
impl From<wasmparser::TagType> for Tag {
    fn from(ty: wasmparser::TagType) -> Tag {
        match ty.kind {
            wasmparser::TagKind::Exception => Tag {
                ty: TypeIndex::from_u32(ty.func_type_idx),
            },
        }
    }
}