1#![forbid(unsafe_code)]
2#![doc = include_str!("../README.md")]
3
4use core::{fmt, str::FromStr};
5use std::error::Error;
6
7#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
9pub enum WasmValueTypeError {
10 Empty,
12 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#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
29pub enum WasmValueType {
30 #[default]
32 I32,
33 I64,
35 F32,
37 F64,
39 V128,
41 FuncRef,
43 ExternRef,
45}
46
47impl WasmValueType {
48 #[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 #[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 #[must_use]
78 pub const fn is_numeric(self) -> bool {
79 matches!(self, Self::I32 | Self::I64 | Self::F32 | Self::F64)
80 }
81
82 #[must_use]
84 pub const fn is_reference(self) -> bool {
85 matches!(self, Self::FuncRef | Self::ExternRef)
86 }
87
88 #[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}