wasm_encoder/core/
producers.rs

1use crate::{CustomSection, Encode, Section, SectionId};
2use alloc::borrow::Cow;
3use alloc::vec::Vec;
4
5/// An encoder for the [producers custom
6/// section](https://github.com/WebAssembly/tool-conventions/blob/main/ProducersSection.md).
7///
8/// This section is a non-standard convention that is supported by many toolchains.
9///
10/// # Example
11///
12/// ```
13/// use wasm_encoder::{ProducersSection, ProducersField, Module};
14///
15/// // Create a new producers section.
16/// let mut field = ProducersField::new();
17/// field.value("clang", "14.0.4");
18/// field.value("rustc", "1.66.1 (90743e729 2023-01-10)");
19/// let mut producers = ProducersSection::new();
20/// producers.field("processed-by", &field);
21///
22/// // Add the producers section to a new Wasm module and get the encoded bytes.
23/// let mut module = Module::new();
24/// module.section(&producers);
25/// let wasm_bytes = module.finish();
26/// ```
27#[derive(Clone, Debug)]
28pub struct ProducersSection {
29    bytes: Vec<u8>,
30    num_fields: u32,
31}
32
33impl ProducersSection {
34    /// Construct an empty encoder for the producers custom section.
35    pub fn new() -> Self {
36        Self::default()
37    }
38
39    /// Add a field to the section. The spec recommends names for this section
40    /// are "language", "processed-by", and "sdk".  Each field in section must
41    /// have a unique name.
42    pub fn field(&mut self, name: &str, values: &ProducersField) -> &mut Self {
43        name.encode(&mut self.bytes);
44        values.encode(&mut self.bytes);
45        self.num_fields += 1;
46        self
47    }
48}
49
50impl Default for ProducersSection {
51    fn default() -> Self {
52        Self {
53            bytes: Vec::new(),
54            num_fields: 0,
55        }
56    }
57}
58
59impl Encode for ProducersSection {
60    fn encode(&self, sink: &mut Vec<u8>) {
61        let mut data = Vec::new();
62        self.num_fields.encode(&mut data);
63        data.extend(&self.bytes);
64
65        CustomSection {
66            name: "producers".into(),
67            data: Cow::Borrowed(&data),
68        }
69        .encode(sink);
70    }
71}
72
73impl Section for ProducersSection {
74    fn id(&self) -> u8 {
75        SectionId::Custom.into()
76    }
77}
78
79/// The value of a field in the producers custom section
80#[derive(Clone, Debug)]
81pub struct ProducersField {
82    bytes: Vec<u8>,
83    num_values: u32,
84}
85
86impl ProducersField {
87    /// Construct an empty encoder for a producers field value
88    pub fn new() -> Self {
89        ProducersField::default()
90    }
91
92    /// Add a value to the field encoder. Each value in a field must have a
93    /// unique name. If there is no sensible value for `version`, use the
94    /// empty string.
95    pub fn value(&mut self, name: &str, version: &str) -> &mut Self {
96        name.encode(&mut self.bytes);
97        version.encode(&mut self.bytes);
98        self.num_values += 1;
99        self
100    }
101}
102
103impl Default for ProducersField {
104    fn default() -> Self {
105        Self {
106            bytes: Vec::new(),
107            num_values: 0,
108        }
109    }
110}
111
112impl Encode for ProducersField {
113    fn encode(&self, sink: &mut Vec<u8>) {
114        self.num_values.encode(sink);
115        sink.extend(&self.bytes);
116    }
117}
118
119#[cfg(test)]
120mod test {
121    #[test]
122    fn roundtrip_example() {
123        use crate::{Module, ProducersField, ProducersSection};
124        use wasmparser::{KnownCustom, Parser, Payload};
125
126        // Create a new producers section.
127        let mut field = ProducersField::new();
128        field.value("clang", "14.0.4");
129        field.value("rustc", "1.66.1");
130        let mut producers = ProducersSection::new();
131        producers.field("processed-by", &field);
132
133        // Add the producers section to a new Wasm module and get the encoded bytes.
134        let mut module = Module::new();
135        module.section(&producers);
136        let wasm_bytes = module.finish();
137
138        let mut parser = Parser::new(0).parse_all(&wasm_bytes);
139        let payload = parser
140            .next()
141            .expect("parser is not empty")
142            .expect("element is a payload");
143        match payload {
144            Payload::Version { .. } => {}
145            _ => panic!(""),
146        }
147        let payload = parser
148            .next()
149            .expect("parser is not empty")
150            .expect("element is a payload");
151        match payload {
152            Payload::CustomSection(c) => {
153                assert_eq!(c.name(), "producers");
154                let mut section = match c.as_known() {
155                    KnownCustom::Producers(s) => s.into_iter(),
156                    _ => panic!("unknown custom section"),
157                };
158                let field = section
159                    .next()
160                    .expect("section has an element")
161                    .expect("element is a producers field");
162                assert_eq!(field.name, "processed-by");
163                let mut values = field.values.into_iter();
164                let value = values
165                    .next()
166                    .expect("values has an element")
167                    .expect("element is a producers field value");
168                assert_eq!(value.name, "clang");
169                assert_eq!(value.version, "14.0.4");
170
171                let value = values
172                    .next()
173                    .expect("values has another element")
174                    .expect("element is a producers field value");
175                assert_eq!(value.name, "rustc");
176                assert_eq!(value.version, "1.66.1");
177            }
178            _ => panic!("unexpected payload"),
179        }
180    }
181}