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