wasm_metadata/
payload.rs

1use std::ops::Range;
2
3use anyhow::Result;
4use serde_derive::Serialize;
5use wasmparser::{KnownCustom, Parser, Payload::*};
6
7use crate::{
8    Authors, ComponentNames, Description, Homepage, Licenses, Metadata, ModuleNames, Producers,
9    Revision, Source,
10};
11
12/// Data representing either a Wasm Component or module
13///
14/// Each payload has additional [`Metadata`] associated with it,
15/// but if it's a Component it may have also additional `Payloads` associated
16/// with it.
17#[derive(Debug, Serialize)]
18#[serde(rename_all = "lowercase")]
19pub enum Payload {
20    /// A representation of a Wasm Component
21    Component {
22        /// The metadata associated with the Component
23        metadata: Metadata,
24        /// The metadata of nested Components or Modules
25        children: Vec<Payload>,
26    },
27    /// A representation of a Wasm Module
28    Module(Metadata),
29}
30
31impl Payload {
32    /// Parse metadata from a WebAssembly binary. Supports both core WebAssembly modules, and
33    /// WebAssembly components.
34    pub fn from_binary(input: &[u8]) -> Result<Self> {
35        let mut output = Vec::new();
36
37        for payload in Parser::new(0).parse_all(&input) {
38            match payload? {
39                Version { encoding, .. } => {
40                    if output.is_empty() {
41                        match encoding {
42                            wasmparser::Encoding::Module => {
43                                output.push(Self::empty_module(0..input.len()))
44                            }
45                            wasmparser::Encoding::Component => {
46                                output.push(Self::empty_component(0..input.len()))
47                            }
48                        }
49                    }
50                }
51                ModuleSection {
52                    unchecked_range: range,
53                    ..
54                } => output.push(Self::empty_module(range)),
55                ComponentSection {
56                    unchecked_range: range,
57                    ..
58                } => output.push(Self::empty_component(range)),
59                End { .. } => {
60                    let finished = output.pop().expect("non-empty metadata stack");
61                    if output.is_empty() {
62                        return Ok(finished);
63                    } else {
64                        output.last_mut().unwrap().push_child(finished);
65                    }
66                }
67                CustomSection(c) => match c.as_known() {
68                    KnownCustom::Name(_) => {
69                        let names = ModuleNames::from_bytes(c.data(), c.data_offset())?;
70                        if let Some(name) = names.get_name() {
71                            output
72                                .last_mut()
73                                .expect("non-empty metadata stack")
74                                .metadata_mut()
75                                .name = Some(name.clone());
76                        }
77                    }
78                    KnownCustom::ComponentName(_) => {
79                        let names = ComponentNames::from_bytes(c.data(), c.data_offset())?;
80                        if let Some(name) = names.get_name() {
81                            output
82                                .last_mut()
83                                .expect("non-empty metadata stack")
84                                .metadata_mut()
85                                .name = Some(name.clone());
86                        }
87                    }
88                    KnownCustom::Producers(_) => {
89                        let producers = Producers::from_bytes(c.data(), c.data_offset())?;
90                        output
91                            .last_mut()
92                            .expect("non-empty metadata stack")
93                            .metadata_mut()
94                            .producers = Some(producers);
95                    }
96                    #[cfg(feature = "oci")]
97                    KnownCustom::Unknown if c.name() == "authors" => {
98                        let a = Authors::parse_custom_section(&c)?;
99                        let Metadata { authors, .. } = output
100                            .last_mut()
101                            .expect("non-empty metadata stack")
102                            .metadata_mut();
103                        *authors = Some(a);
104                    }
105                    #[cfg(feature = "oci")]
106                    KnownCustom::Unknown if c.name() == "description" => {
107                        let a = Description::parse_custom_section(&c)?;
108                        let Metadata { description, .. } = output
109                            .last_mut()
110                            .expect("non-empty metadata stack")
111                            .metadata_mut();
112                        *description = Some(a);
113                    }
114                    #[cfg(feature = "oci")]
115                    KnownCustom::Unknown if c.name() == "licenses" => {
116                        let a = Licenses::parse_custom_section(&c)?;
117                        let Metadata { licenses, .. } = output
118                            .last_mut()
119                            .expect("non-empty metadata stack")
120                            .metadata_mut();
121                        *licenses = Some(a);
122                    }
123                    #[cfg(feature = "oci")]
124                    KnownCustom::Unknown if c.name() == "source" => {
125                        let a = Source::parse_custom_section(&c)?;
126                        let Metadata { source, .. } = output
127                            .last_mut()
128                            .expect("non-empty metadata stack")
129                            .metadata_mut();
130                        *source = Some(a);
131                    }
132                    #[cfg(feature = "oci")]
133                    KnownCustom::Unknown if c.name() == "homepage" => {
134                        let a = Homepage::parse_custom_section(&c)?;
135                        let Metadata { homepage, .. } = output
136                            .last_mut()
137                            .expect("non-empty metadata stack")
138                            .metadata_mut();
139                        *homepage = Some(a);
140                    }
141                    #[cfg(feature = "oci")]
142                    KnownCustom::Unknown if c.name() == "revision" => {
143                        let a = Revision::parse_custom_section(&c)?;
144                        let Metadata { revision, .. } = output
145                            .last_mut()
146                            .expect("non-empty metadata stack")
147                            .metadata_mut();
148                        *revision = Some(a);
149                    }
150                    #[cfg(feature = "oci")]
151                    KnownCustom::Unknown if c.name() == "version" => {
152                        let a = crate::Version::parse_custom_section(&c)?;
153                        let Metadata { version, .. } = output
154                            .last_mut()
155                            .expect("non-empty metadata stack")
156                            .metadata_mut();
157                        *version = Some(a);
158                    }
159                    #[cfg(feature = "oci")]
160                    KnownCustom::Unknown if c.name() == ".dep-v0" => {
161                        let a = crate::Dependencies::parse_custom_section(&c)?;
162                        let Metadata { dependencies, .. } = output
163                            .last_mut()
164                            .expect("non-empty metadata stack")
165                            .metadata_mut();
166                        *dependencies = Some(a);
167                    }
168                    _ => {}
169                },
170                _ => {}
171            }
172        }
173        Err(anyhow::anyhow!(
174            "malformed wasm binary, should have reached end"
175        ))
176    }
177
178    /// Get a reference te the metadata
179    pub fn metadata(&self) -> &Metadata {
180        match self {
181            Payload::Component { metadata, .. } => metadata,
182            Payload::Module(metadata) => metadata,
183        }
184    }
185
186    /// Get a mutable reference te the metadata
187    pub fn metadata_mut(&mut self) -> &mut Metadata {
188        match self {
189            Payload::Component { metadata, .. } => metadata,
190            Payload::Module(metadata) => metadata,
191        }
192    }
193
194    fn empty_component(range: Range<usize>) -> Self {
195        let mut this = Self::Component {
196            metadata: Metadata::default(),
197            children: vec![],
198        };
199        this.metadata_mut().range = range;
200        this
201    }
202
203    fn empty_module(range: Range<usize>) -> Self {
204        let mut this = Self::Module(Metadata::default());
205        this.metadata_mut().range = range;
206        this
207    }
208
209    fn push_child(&mut self, child: Self) {
210        match self {
211            Self::Module { .. } => panic!("module shouldnt have children"),
212            Self::Component { children, .. } => children.push(child),
213        }
214    }
215}