Skip to main content

use_wasm_value/
lib.rs

1#![forbid(unsafe_code)]
2#![doc = include_str!("../README.md")]
3
4use core::{fmt, str::FromStr};
5use std::error::Error;
6
7/// Error returned when a WebAssembly value type cannot be parsed.
8#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
9pub enum WasmValueTypeError {
10    /// The supplied label was empty.
11    Empty,
12    /// The supplied label or byte code is not a known value type.
13    Unknown,
14}
15
16impl fmt::Display for WasmValueTypeError {
17    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
18        match self {
19            Self::Empty => formatter.write_str("WebAssembly value type cannot be empty"),
20            Self::Unknown => formatter.write_str("unknown WebAssembly value type"),
21        }
22    }
23}
24
25impl Error for WasmValueTypeError {}
26
27/// Core WebAssembly value types.
28#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
29pub enum WasmValueType {
30    /// 32-bit integer.
31    #[default]
32    I32,
33    /// 64-bit integer.
34    I64,
35    /// 32-bit float.
36    F32,
37    /// 64-bit float.
38    F64,
39    /// 128-bit vector.
40    V128,
41    /// Function reference.
42    FuncRef,
43    /// External reference.
44    ExternRef,
45}
46
47impl WasmValueType {
48    /// Returns the canonical value type label.
49    #[must_use]
50    pub const fn as_str(self) -> &'static str {
51        match self {
52            Self::I32 => "i32",
53            Self::I64 => "i64",
54            Self::F32 => "f32",
55            Self::F64 => "f64",
56            Self::V128 => "v128",
57            Self::FuncRef => "funcref",
58            Self::ExternRef => "externref",
59        }
60    }
61
62    /// Returns the binary value type code.
63    #[must_use]
64    pub const fn code(self) -> u8 {
65        match self {
66            Self::I32 => 0x7f,
67            Self::I64 => 0x7e,
68            Self::F32 => 0x7d,
69            Self::F64 => 0x7c,
70            Self::V128 => 0x7b,
71            Self::FuncRef => 0x70,
72            Self::ExternRef => 0x6f,
73        }
74    }
75
76    /// Returns 'true' for numeric scalar value types.
77    #[must_use]
78    pub const fn is_numeric(self) -> bool {
79        matches!(self, Self::I32 | Self::I64 | Self::F32 | Self::F64)
80    }
81
82    /// Returns 'true' for reference value types.
83    #[must_use]
84    pub const fn is_reference(self) -> bool {
85        matches!(self, Self::FuncRef | Self::ExternRef)
86    }
87
88    /// Returns 'true' for vector value types.
89    #[must_use]
90    pub const fn is_vector(self) -> bool {
91        matches!(self, Self::V128)
92    }
93}
94
95impl fmt::Display for WasmValueType {
96    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
97        formatter.write_str(self.as_str())
98    }
99}
100
101impl FromStr for WasmValueType {
102    type Err = WasmValueTypeError;
103
104    fn from_str(value: &str) -> Result<Self, Self::Err> {
105        let trimmed = value.trim();
106        if trimmed.is_empty() {
107            return Err(WasmValueTypeError::Empty);
108        }
109        let normalized = trimmed.to_ascii_lowercase().replace(['-', '_'], "");
110        match normalized.as_str() {
111            "i32" => Ok(Self::I32),
112            "i64" => Ok(Self::I64),
113            "f32" => Ok(Self::F32),
114            "f64" => Ok(Self::F64),
115            "v128" => Ok(Self::V128),
116            "funcref" => Ok(Self::FuncRef),
117            "externref" => Ok(Self::ExternRef),
118            _ => Err(WasmValueTypeError::Unknown),
119        }
120    }
121}
122
123impl TryFrom<u8> for WasmValueType {
124    type Error = WasmValueTypeError;
125
126    fn try_from(value: u8) -> Result<Self, Self::Error> {
127        match value {
128            0x7f => Ok(Self::I32),
129            0x7e => Ok(Self::I64),
130            0x7d => Ok(Self::F32),
131            0x7c => Ok(Self::F64),
132            0x7b => Ok(Self::V128),
133            0x70 => Ok(Self::FuncRef),
134            0x6f => Ok(Self::ExternRef),
135            _ => Err(WasmValueTypeError::Unknown),
136        }
137    }
138}
139
140#[cfg(test)]
141mod tests {
142    use super::{WasmValueType, WasmValueTypeError};
143
144    #[test]
145    fn parses_value_types() {
146        assert_eq!("i32".parse::<WasmValueType>(), Ok(WasmValueType::I32));
147        assert_eq!(
148            "func-ref".parse::<WasmValueType>(),
149            Ok(WasmValueType::FuncRef)
150        );
151        assert_eq!("".parse::<WasmValueType>(), Err(WasmValueTypeError::Empty));
152    }
153
154    #[test]
155    fn classifies_and_renders_types() {
156        assert!(WasmValueType::I64.is_numeric());
157        assert!(WasmValueType::ExternRef.is_reference());
158        assert!(WasmValueType::V128.is_vector());
159        assert_eq!(WasmValueType::F32.code(), 0x7d);
160        assert_eq!(WasmValueType::F64.to_string(), "f64");
161    }
162}