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 WasmFeatureError {
10 Empty,
12 Unknown,
14}
15
16impl fmt::Display for WasmFeatureError {
17 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
18 match self {
19 Self::Empty => formatter.write_str("WebAssembly feature label cannot be empty"),
20 Self::Unknown => formatter.write_str("unknown WebAssembly feature label"),
21 }
22 }
23}
24
25impl Error for WasmFeatureError {}
26
27#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
29pub enum WasmFeatureStatus {
30 #[default]
32 Stable,
33 Experimental,
35 Deprecated,
37}
38
39impl WasmFeatureStatus {
40 #[must_use]
42 pub const fn as_str(self) -> &'static str {
43 match self {
44 Self::Stable => "stable",
45 Self::Experimental => "experimental",
46 Self::Deprecated => "deprecated",
47 }
48 }
49}
50
51impl fmt::Display for WasmFeatureStatus {
52 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
53 formatter.write_str(self.as_str())
54 }
55}
56
57#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
59pub enum WasmFeature {
60 #[default]
62 Simd,
63 Threads,
65 ReferenceTypes,
67 BulkMemory,
69 TailCalls,
71 Exceptions,
73 Gc,
75 Memory64,
77 MultiValue,
79 ComponentModel,
81}
82
83impl WasmFeature {
84 #[must_use]
86 pub const fn as_str(self) -> &'static str {
87 match self {
88 Self::Simd => "simd",
89 Self::Threads => "threads",
90 Self::ReferenceTypes => "reference-types",
91 Self::BulkMemory => "bulk-memory",
92 Self::TailCalls => "tail-calls",
93 Self::Exceptions => "exceptions",
94 Self::Gc => "gc",
95 Self::Memory64 => "memory64",
96 Self::MultiValue => "multi-value",
97 Self::ComponentModel => "component-model",
98 }
99 }
100
101 #[must_use]
103 pub const fn status(self) -> WasmFeatureStatus {
104 match self {
105 Self::Exceptions | Self::Gc | Self::Memory64 | Self::TailCalls => {
106 WasmFeatureStatus::Experimental
107 },
108 Self::Simd
109 | Self::Threads
110 | Self::ReferenceTypes
111 | Self::BulkMemory
112 | Self::MultiValue
113 | Self::ComponentModel => WasmFeatureStatus::Stable,
114 }
115 }
116
117 #[must_use]
119 pub const fn is_stable(self) -> bool {
120 matches!(self.status(), WasmFeatureStatus::Stable)
121 }
122}
123
124impl fmt::Display for WasmFeature {
125 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
126 formatter.write_str(self.as_str())
127 }
128}
129
130impl FromStr for WasmFeature {
131 type Err = WasmFeatureError;
132
133 fn from_str(value: &str) -> Result<Self, Self::Err> {
134 let trimmed = value.trim();
135 if trimmed.is_empty() {
136 return Err(WasmFeatureError::Empty);
137 }
138 let normalized: String = trimmed
139 .chars()
140 .map(|character| {
141 if character == '_' || character.is_whitespace() {
142 '-'
143 } else {
144 character.to_ascii_lowercase()
145 }
146 })
147 .collect();
148 match normalized.as_str() {
149 "simd" => Ok(Self::Simd),
150 "threads" => Ok(Self::Threads),
151 "reference-types" => Ok(Self::ReferenceTypes),
152 "bulk-memory" => Ok(Self::BulkMemory),
153 "tail-calls" => Ok(Self::TailCalls),
154 "exceptions" => Ok(Self::Exceptions),
155 "gc" => Ok(Self::Gc),
156 "memory64" => Ok(Self::Memory64),
157 "multi-value" => Ok(Self::MultiValue),
158 "component-model" => Ok(Self::ComponentModel),
159 _ => Err(WasmFeatureError::Unknown),
160 }
161 }
162}
163
164#[cfg(test)]
165mod tests {
166 use super::{WasmFeature, WasmFeatureError, WasmFeatureStatus};
167
168 #[test]
169 fn parses_feature_labels() {
170 assert_eq!(
171 "bulk memory".parse::<WasmFeature>(),
172 Ok(WasmFeature::BulkMemory)
173 );
174 assert_eq!("".parse::<WasmFeature>(), Err(WasmFeatureError::Empty));
175 }
176
177 #[test]
178 fn exposes_status_labels() {
179 assert!(WasmFeature::Simd.is_stable());
180 assert_eq!(
181 WasmFeature::Memory64.status(),
182 WasmFeatureStatus::Experimental
183 );
184 assert_eq!(WasmFeature::ComponentModel.to_string(), "component-model");
185 assert_eq!(WasmFeatureStatus::Stable.to_string(), "stable");
186 }
187}