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 WasmSectionError {
10 Empty,
12 UnknownId,
14 UnknownName,
16}
17
18impl fmt::Display for WasmSectionError {
19 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
20 match self {
21 Self::Empty => formatter.write_str("WebAssembly section label cannot be empty"),
22 Self::UnknownId => formatter.write_str("unknown WebAssembly section ID"),
23 Self::UnknownName => formatter.write_str("unknown WebAssembly section label"),
24 }
25 }
26}
27
28impl Error for WasmSectionError {}
29
30#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
32pub enum WasmSectionKind {
33 #[default]
35 Custom,
36 Type,
38 Import,
40 Function,
42 Table,
44 Memory,
46 Global,
48 Export,
50 Start,
52 Element,
54 Code,
56 Data,
58 DataCount,
60}
61
62impl WasmSectionKind {
63 #[must_use]
65 pub const fn id(self) -> u8 {
66 match self {
67 Self::Custom => 0,
68 Self::Type => 1,
69 Self::Import => 2,
70 Self::Function => 3,
71 Self::Table => 4,
72 Self::Memory => 5,
73 Self::Global => 6,
74 Self::Export => 7,
75 Self::Start => 8,
76 Self::Element => 9,
77 Self::Code => 10,
78 Self::Data => 11,
79 Self::DataCount => 12,
80 }
81 }
82
83 #[must_use]
85 pub const fn as_str(self) -> &'static str {
86 match self {
87 Self::Custom => "custom",
88 Self::Type => "type",
89 Self::Import => "import",
90 Self::Function => "function",
91 Self::Table => "table",
92 Self::Memory => "memory",
93 Self::Global => "global",
94 Self::Export => "export",
95 Self::Start => "start",
96 Self::Element => "element",
97 Self::Code => "code",
98 Self::Data => "data",
99 Self::DataCount => "data-count",
100 }
101 }
102
103 #[must_use]
105 pub const fn is_custom(self) -> bool {
106 matches!(self, Self::Custom)
107 }
108}
109
110impl fmt::Display for WasmSectionKind {
111 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
112 formatter.write_str(self.as_str())
113 }
114}
115
116impl TryFrom<u8> for WasmSectionKind {
117 type Error = WasmSectionError;
118
119 fn try_from(value: u8) -> Result<Self, Self::Error> {
120 match value {
121 0 => Ok(Self::Custom),
122 1 => Ok(Self::Type),
123 2 => Ok(Self::Import),
124 3 => Ok(Self::Function),
125 4 => Ok(Self::Table),
126 5 => Ok(Self::Memory),
127 6 => Ok(Self::Global),
128 7 => Ok(Self::Export),
129 8 => Ok(Self::Start),
130 9 => Ok(Self::Element),
131 10 => Ok(Self::Code),
132 11 => Ok(Self::Data),
133 12 => Ok(Self::DataCount),
134 _ => Err(WasmSectionError::UnknownId),
135 }
136 }
137}
138
139impl FromStr for WasmSectionKind {
140 type Err = WasmSectionError;
141
142 fn from_str(value: &str) -> Result<Self, Self::Err> {
143 let trimmed = value.trim();
144 if trimmed.is_empty() {
145 return Err(WasmSectionError::Empty);
146 }
147 let normalized: String = trimmed
148 .chars()
149 .map(|character| {
150 if character == '_' || character.is_whitespace() {
151 '-'
152 } else {
153 character.to_ascii_lowercase()
154 }
155 })
156 .collect();
157
158 match normalized.as_str() {
159 "custom" => Ok(Self::Custom),
160 "type" => Ok(Self::Type),
161 "import" => Ok(Self::Import),
162 "function" => Ok(Self::Function),
163 "table" => Ok(Self::Table),
164 "memory" => Ok(Self::Memory),
165 "global" => Ok(Self::Global),
166 "export" => Ok(Self::Export),
167 "start" => Ok(Self::Start),
168 "element" => Ok(Self::Element),
169 "code" => Ok(Self::Code),
170 "data" => Ok(Self::Data),
171 "data-count" | "datacount" => Ok(Self::DataCount),
172 _ => Err(WasmSectionError::UnknownName),
173 }
174 }
175}
176
177#[cfg(test)]
178mod tests {
179 use super::{WasmSectionError, WasmSectionKind};
180
181 #[test]
182 fn parses_section_ids() {
183 assert_eq!(WasmSectionKind::try_from(0), Ok(WasmSectionKind::Custom));
184 assert_eq!(
185 WasmSectionKind::try_from(12),
186 Ok(WasmSectionKind::DataCount)
187 );
188 assert_eq!(
189 WasmSectionKind::try_from(99),
190 Err(WasmSectionError::UnknownId)
191 );
192 }
193
194 #[test]
195 fn parses_and_renders_labels() {
196 let section = "data count"
197 .parse::<WasmSectionKind>()
198 .expect("known section");
199
200 assert_eq!(section, WasmSectionKind::DataCount);
201 assert_eq!(section.id(), 12);
202 assert_eq!(section.to_string(), "data-count");
203 assert!(!section.is_custom());
204 }
205}