wasm_metadata/
producers.rs1use anyhow::Result;
2use indexmap::{map::Entry, IndexMap};
3use wasm_encoder::Encode;
4use wasmparser::{BinaryReader, KnownCustom, Parser, ProducersSectionReader};
5
6use crate::{rewrite_wasm, AddMetadata};
7#[derive(Debug)]
11#[cfg_attr(feature = "serde", derive(serde_derive::Serialize))]
12pub struct Producers(
13 #[cfg_attr(
14 feature = "serde",
15 serde(serialize_with = "indexmap::map::serde_seq::serialize")
16 )]
17 IndexMap<String, IndexMap<String, String>>,
18);
19
20impl Default for Producers {
21 fn default() -> Self {
22 Self::empty()
23 }
24}
25
26impl Producers {
27 pub fn empty() -> Self {
29 Producers(IndexMap::new())
30 }
31
32 pub fn is_empty(&self) -> bool {
34 self.0.is_empty()
35 }
36
37 pub fn from_wasm(bytes: &[u8]) -> Result<Option<Self>> {
42 let mut depth = 0;
43 for payload in Parser::new(0).parse_all(bytes) {
44 let payload = payload?;
45 use wasmparser::Payload::*;
46 match payload {
47 ModuleSection { .. } | ComponentSection { .. } => depth += 1,
48 End { .. } => depth -= 1,
49 CustomSection(c) if depth == 0 => {
50 if let KnownCustom::Producers(_) = c.as_known() {
51 let producers = Self::from_bytes(c.data(), c.data_offset())?;
52 return Ok(Some(producers));
53 }
54 }
55 _ => {}
56 }
57 }
58 Ok(None)
59 }
60 pub fn from_bytes(bytes: &[u8], offset: usize) -> Result<Self> {
62 let reader = BinaryReader::new(bytes, offset);
63 let section = ProducersSectionReader::new(reader)?;
64 let mut fields = IndexMap::new();
65 for field in section.into_iter() {
66 let field = field?;
67 let mut values = IndexMap::new();
68 for value in field.values.into_iter() {
69 let value = value?;
70 values.insert(value.name.to_owned(), value.version.to_owned());
71 }
72 fields.insert(field.name.to_owned(), values);
73 }
74 Ok(Producers(fields))
75 }
76 pub fn add(&mut self, field: &str, name: &str, version: &str) {
81 match self.0.entry(field.to_string()) {
82 Entry::Occupied(e) => {
83 e.into_mut().insert(name.to_owned(), version.to_owned());
84 }
85 Entry::Vacant(e) => {
86 let mut m = IndexMap::new();
87 m.insert(name.to_owned(), version.to_owned());
88 e.insert(m);
89 }
90 }
91 }
92
93 pub fn merge(&mut self, other: &Self) {
96 for (field, values) in other.iter() {
97 for (name, version) in values.iter() {
98 self.add(field, name, version);
99 }
100 }
101 }
102
103 pub fn get<'a>(&'a self, field: &str) -> Option<ProducersField<'a>> {
105 self.0.get(&field.to_owned()).map(ProducersField)
106 }
107
108 pub fn iter<'a>(&'a self) -> impl Iterator<Item = (&'a String, ProducersField<'a>)> + 'a {
110 self.0
111 .iter()
112 .map(|(name, field)| (name, ProducersField(field)))
113 }
114
115 pub(crate) fn from_meta(add: &AddMetadata) -> Self {
117 let mut s = Self::empty();
118 for (lang, version) in add.language.iter() {
119 s.add("language", &lang, &version);
120 }
121 for (name, version) in add.processed_by.iter() {
122 s.add("processed-by", &name, &version);
123 }
124 for (name, version) in add.sdk.iter() {
125 s.add("sdk", &name, &version);
126 }
127 s
128 }
129
130 pub(crate) fn section(&self) -> wasm_encoder::ProducersSection {
132 let mut section = wasm_encoder::ProducersSection::new();
133 for (fieldname, fieldvalues) in self.0.iter() {
134 let mut field = wasm_encoder::ProducersField::new();
135 for (name, version) in fieldvalues {
136 field.value(&name, &version);
137 }
138 section.field(&fieldname, &field);
139 }
140 section
141 }
142
143 pub fn raw_custom_section(&self) -> Vec<u8> {
145 let mut ret = Vec::new();
146 self.section().encode(&mut ret);
147 ret
148 }
149
150 pub fn add_to_wasm(&self, input: &[u8]) -> Result<Vec<u8>> {
153 rewrite_wasm(&Default::default(), self, input)
154 }
155}
156
157#[derive(Debug)]
159pub struct ProducersField<'a>(&'a IndexMap<String, String>);
160
161impl<'a> ProducersField<'a> {
162 pub fn get(&self, name: &str) -> Option<&'a String> {
164 self.0.get(&name.to_owned())
165 }
166 pub fn iter(&self) -> impl Iterator<Item = (&'a String, &'a String)> + 'a {
168 self.0.iter()
169 }
170}
171
172#[cfg(test)]
173mod test {
174 use super::*;
175 use crate::{Metadata, Payload};
176 use wasm_encoder::Module;
177
178 #[test]
179 fn producers_empty_module() {
180 let module = Module::new().finish();
181 let mut producers = Producers::empty();
182 producers.add("language", "bar", "");
183 producers.add("processed-by", "baz", "1.0");
184
185 let module = producers.add_to_wasm(&module).unwrap();
186
187 match Payload::from_binary(&module).unwrap() {
188 Payload::Module(Metadata {
189 name, producers, ..
190 }) => {
191 assert_eq!(name, None);
192 let producers = producers.expect("some producers");
193 assert_eq!(producers.get("language").unwrap().get("bar").unwrap(), "");
194 assert_eq!(
195 producers.get("processed-by").unwrap().get("baz").unwrap(),
196 "1.0"
197 );
198 }
199 _ => panic!("metadata should be module"),
200 }
201 }
202
203 #[test]
204 fn producers_add_another_field() {
205 let module = Module::new().finish();
206 let mut producers = Producers::empty();
207 producers.add("language", "bar", "");
208 producers.add("processed-by", "baz", "1.0");
209 let module = producers.add_to_wasm(&module).unwrap();
210
211 let mut producers = Producers::empty();
212 producers.add("language", "waaat", "");
213 let module = producers.add_to_wasm(&module).unwrap();
214
215 match Payload::from_binary(&module).unwrap() {
216 Payload::Module(Metadata {
217 name, producers, ..
218 }) => {
219 assert_eq!(name, None);
220 let producers = producers.expect("some producers");
221 assert_eq!(producers.get("language").unwrap().get("bar").unwrap(), "");
222 assert_eq!(producers.get("language").unwrap().get("waaat").unwrap(), "");
223 assert_eq!(
224 producers.get("processed-by").unwrap().get("baz").unwrap(),
225 "1.0"
226 );
227 }
228 _ => panic!("metadata should be module"),
229 }
230 }
231
232 #[test]
233 fn producers_overwrite_field() {
234 let module = Module::new().finish();
235 let mut producers = Producers::empty();
236 producers.add("processed-by", "baz", "1.0");
237 let module = producers.add_to_wasm(&module).unwrap();
238
239 let mut producers = Producers::empty();
240 producers.add("processed-by", "baz", "420");
241 let module = producers.add_to_wasm(&module).unwrap();
242
243 match Payload::from_binary(&module).unwrap() {
244 Payload::Module(Metadata { producers, .. }) => {
245 let producers = producers.expect("some producers");
246 assert_eq!(
247 producers.get("processed-by").unwrap().get("baz").unwrap(),
248 "420"
249 );
250 }
251 _ => panic!("metadata should be module"),
252 }
253 }
254}