Skip to main content

use_wasm_section/
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 section label or ID is invalid.
8#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
9pub enum WasmSectionError {
10    /// The supplied section label was empty.
11    Empty,
12    /// The supplied section ID is not assigned by the core WebAssembly format.
13    UnknownId,
14    /// The supplied section label was not recognized.
15    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/// Known WebAssembly section kinds.
31#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
32pub enum WasmSectionKind {
33    /// Section ID 0.
34    #[default]
35    Custom,
36    /// Section ID 1.
37    Type,
38    /// Section ID 2.
39    Import,
40    /// Section ID 3.
41    Function,
42    /// Section ID 4.
43    Table,
44    /// Section ID 5.
45    Memory,
46    /// Section ID 6.
47    Global,
48    /// Section ID 7.
49    Export,
50    /// Section ID 8.
51    Start,
52    /// Section ID 9.
53    Element,
54    /// Section ID 10.
55    Code,
56    /// Section ID 11.
57    Data,
58    /// Section ID 12.
59    DataCount,
60}
61
62impl WasmSectionKind {
63    /// Returns the numeric section ID.
64    #[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    /// Returns the stable section label.
84    #[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    /// Returns 'true' for custom sections.
104    #[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}