Skip to content

Commit 1bcecbc

Browse files
authored
[wasm-metadata] add support for Homepage custom section (#1945)
1 parent 2efb389 commit 1bcecbc

File tree

10 files changed

+179
-9
lines changed

10 files changed

+179
-9
lines changed

crates/wasm-metadata/src/add_metadata.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use crate::{rewrite_wasm, Author, Description, Licenses, Producers, Source};
1+
use crate::{rewrite_wasm, Author, Description, Homepage, Licenses, Producers, Source};
22

33
use anyhow::Result;
44

@@ -41,6 +41,10 @@ pub struct AddMetadata {
4141
/// URL to get source code for building the image
4242
#[cfg_attr(feature = "clap", clap(long, value_name = "NAME"))]
4343
pub source: Option<Source>,
44+
45+
/// URL to find more information on the binary
46+
#[cfg_attr(feature = "clap", clap(long, value_name = "NAME"))]
47+
pub homepage: Option<Homepage>,
4448
}
4549

4650
#[cfg(feature = "clap")]
@@ -62,6 +66,7 @@ impl AddMetadata {
6266
&self.description,
6367
&self.licenses,
6468
&self.source,
69+
&self.homepage,
6570
input,
6671
)
6772
}

crates/wasm-metadata/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
pub use add_metadata::AddMetadata;
66
pub use metadata::Metadata;
77
pub use names::{ComponentNames, ModuleNames};
8-
pub use oci_annotations::{Author, Description, Licenses, Source};
8+
pub use oci_annotations::{Author, Description, Homepage, Licenses, Source};
99
pub use payload::Payload;
1010
pub use producers::{Producers, ProducersField};
1111

crates/wasm-metadata/src/metadata.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use serde_derive::Serialize;
22
use std::ops::Range;
33

4-
use crate::{Author, Description, Licenses, Producers, Source};
4+
use crate::{Author, Description, Homepage, Licenses, Producers, Source};
55

66
/// Metadata associated with a Wasm Component or Module
77
#[derive(Debug, Serialize, Default)]
@@ -19,6 +19,8 @@ pub struct Metadata {
1919
pub licenses: Option<Licenses>,
2020
/// URL to get source code for building the image
2121
pub source: Option<Source>,
22+
/// URL to find more information on the binary
23+
pub homepage: Option<Homepage>,
2224
/// Byte range of the module in the parent binary
2325
pub range: Range<usize>,
2426
}
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
use std::borrow::Cow;
2+
use std::fmt::{self, Display};
3+
use std::str::FromStr;
4+
5+
use anyhow::{ensure, Error, Result};
6+
use serde::Serialize;
7+
use url::Url;
8+
use wasm_encoder::{ComponentSection, CustomSection, Encode, Section};
9+
use wasmparser::CustomSectionReader;
10+
11+
/// URL to find more information on the binary
12+
#[derive(Debug, Clone, PartialEq)]
13+
pub struct Homepage(CustomSection<'static>);
14+
15+
impl Homepage {
16+
/// Create a new instance of `Homepage`.
17+
pub fn new(s: &str) -> Result<Self> {
18+
Ok(Url::parse(s)?.into())
19+
}
20+
21+
/// Parse a `homepage` custom section from a wasm binary.
22+
pub(crate) fn parse_custom_section(reader: &CustomSectionReader<'_>) -> Result<Self> {
23+
ensure!(
24+
reader.name() == "homepage",
25+
"The `homepage` custom section should have a name of 'homepage'"
26+
);
27+
let data = String::from_utf8(reader.data().to_owned())?;
28+
Self::new(&data)
29+
}
30+
}
31+
32+
impl FromStr for Homepage {
33+
type Err = Error;
34+
35+
fn from_str(s: &str) -> Result<Self, Self::Err> {
36+
Self::new(s)
37+
}
38+
}
39+
40+
impl From<Url> for Homepage {
41+
fn from(expression: Url) -> Self {
42+
Self(CustomSection {
43+
name: "homepage".into(),
44+
data: Cow::Owned(expression.to_string().into_bytes()),
45+
})
46+
}
47+
}
48+
49+
impl Serialize for Homepage {
50+
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
51+
where
52+
S: serde::Serializer,
53+
{
54+
serializer.serialize_str(&self.to_string())
55+
}
56+
}
57+
58+
impl Display for Homepage {
59+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
60+
// NOTE: this will never panic since we always guarantee the data is
61+
// encoded as utf8, even if we internally store it as [u8].
62+
let data = String::from_utf8(self.0.data.to_vec()).unwrap();
63+
write!(f, "{data}")
64+
}
65+
}
66+
67+
impl ComponentSection for Homepage {
68+
fn id(&self) -> u8 {
69+
ComponentSection::id(&self.0)
70+
}
71+
}
72+
73+
impl Section for Homepage {
74+
fn id(&self) -> u8 {
75+
Section::id(&self.0)
76+
}
77+
}
78+
79+
impl Encode for Homepage {
80+
fn encode(&self, sink: &mut Vec<u8>) {
81+
self.0.encode(sink);
82+
}
83+
}
84+
85+
#[cfg(test)]
86+
mod test {
87+
use super::*;
88+
use wasm_encoder::Component;
89+
use wasmparser::Payload;
90+
91+
#[test]
92+
fn roundtrip() {
93+
let mut component = Component::new();
94+
component
95+
.section(&Homepage::new("https://github.com/bytecodealliance/wasm-tools").unwrap());
96+
let component = component.finish();
97+
98+
let mut parsed = false;
99+
for section in wasmparser::Parser::new(0).parse_all(&component) {
100+
if let Payload::CustomSection(reader) = section.unwrap() {
101+
let description = Homepage::parse_custom_section(&reader).unwrap();
102+
assert_eq!(
103+
description.to_string(),
104+
"https://github.com/bytecodealliance/wasm-tools"
105+
);
106+
parsed = true;
107+
}
108+
}
109+
assert!(parsed);
110+
}
111+
112+
#[test]
113+
fn serialize() {
114+
let description = Homepage::new("https://github.com/bytecodealliance/wasm-tools").unwrap();
115+
let json = serde_json::to_string(&description).unwrap();
116+
assert_eq!(r#""https://github.com/bytecodealliance/wasm-tools""#, json);
117+
}
118+
}

crates/wasm-metadata/src/oci_annotations/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,12 @@
1717
1818
pub use author::Author;
1919
pub use description::Description;
20+
pub use homepage::Homepage;
2021
pub use licenses::Licenses;
2122
pub use source::Source;
2223

2324
mod author;
2425
mod description;
26+
mod homepage;
2527
mod licenses;
2628
mod source;

crates/wasm-metadata/src/payload.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@ use serde_derive::Serialize;
66
use wasmparser::{KnownCustom, Parser, Payload::*};
77

88
use crate::{
9-
Author, ComponentNames, Description, Licenses, Metadata, ModuleNames, Producers, Source,
9+
Author, ComponentNames, Description, Homepage, Licenses, Metadata, ModuleNames, Producers,
10+
Source,
1011
};
1112

1213
/// Data representing either a Wasm Component or module
@@ -125,6 +126,14 @@ impl Payload {
125126
.metadata_mut();
126127
*source = Some(a);
127128
}
129+
KnownCustom::Unknown if c.name() == "homepage" => {
130+
let a = Homepage::parse_custom_section(&c)?;
131+
let Metadata { homepage, .. } = output
132+
.last_mut()
133+
.expect("non-empty metadata stack")
134+
.metadata_mut();
135+
*homepage = Some(a);
136+
}
128137
_ => {}
129138
},
130139
_ => {}

crates/wasm-metadata/src/producers.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ impl Producers {
148148
/// Merge into an existing wasm module. Rewrites the module with this producers section
149149
/// merged into its existing one, or adds this producers section if none is present.
150150
pub fn add_to_wasm(&self, input: &[u8]) -> Result<Vec<u8>> {
151-
rewrite_wasm(&None, self, &None, &None, &None, &None, input)
151+
rewrite_wasm(&None, self, &None, &None, &None, &None, &None, input)
152152
}
153153

154154
pub(crate) fn display(&self, f: &mut fmt::Formatter, indent: usize) -> fmt::Result {

crates/wasm-metadata/src/rewrite.rs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
use crate::{Author, ComponentNames, Description, Licenses, ModuleNames, Producers, Source};
1+
use crate::{
2+
Author, ComponentNames, Description, Homepage, Licenses, ModuleNames, Producers, Source,
3+
};
24
use anyhow::Result;
35
use std::mem;
46
use wasm_encoder::ComponentSection as _;
@@ -12,6 +14,7 @@ pub(crate) fn rewrite_wasm(
1214
add_description: &Option<Description>,
1315
add_licenses: &Option<Licenses>,
1416
add_source: &Option<Source>,
17+
add_homepage: &Option<Homepage>,
1518
input: &[u8],
1619
) -> Result<Vec<u8>> {
1720
let mut producers_found = false;
@@ -106,6 +109,13 @@ pub(crate) fn rewrite_wasm(
106109
continue;
107110
}
108111
}
112+
KnownCustom::Unknown if c.name() == "homepage" => {
113+
if add_source.is_none() {
114+
let homepage = Homepage::parse_custom_section(c)?;
115+
homepage.append_to(&mut output);
116+
continue;
117+
}
118+
}
109119
_ => {}
110120
}
111121
}
@@ -147,5 +157,8 @@ pub(crate) fn rewrite_wasm(
147157
if let Some(source) = add_source {
148158
source.append_to(&mut output);
149159
}
160+
if let Some(homepage) = add_homepage {
161+
homepage.append_to(&mut output);
162+
}
150163
Ok(output)
151164
}

crates/wasm-metadata/tests/component.rs

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ fn add_to_empty_component() {
1717
Licenses::new("Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT").unwrap(),
1818
),
1919
source: Some(Source::new("https://github.com/bytecodealliance/wasm-tools").unwrap()),
20+
homepage: Some(Homepage::new("https://github.com/bytecodealliance/wasm-tools").unwrap()),
2021
};
2122
let component = add.to_wasm(&component).unwrap();
2223

@@ -32,6 +33,7 @@ fn add_to_empty_component() {
3233
licenses,
3334
source,
3435
range,
36+
homepage,
3537
},
3638
} => {
3739
assert!(children.is_empty());
@@ -56,9 +58,13 @@ fn add_to_empty_component() {
5658
source.unwrap(),
5759
Source::new("https://github.com/bytecodealliance/wasm-tools").unwrap(),
5860
);
61+
assert_eq!(
62+
homepage.unwrap(),
63+
Homepage::new("https://github.com/bytecodealliance/wasm-tools").unwrap(),
64+
);
5965

6066
assert_eq!(range.start, 0);
61-
assert_eq!(range.end, 251);
67+
assert_eq!(range.end, 308);
6268
}
6369
_ => panic!("metadata should be component"),
6470
}
@@ -79,6 +85,7 @@ fn add_to_nested_component() {
7985
Licenses::new("Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT").unwrap(),
8086
),
8187
source: Some(Source::new("https://github.com/bytecodealliance/wasm-tools").unwrap()),
88+
homepage: Some(Homepage::new("https://github.com/bytecodealliance/wasm-tools").unwrap()),
8289
};
8390
let module = add.to_wasm(&module).unwrap();
8491

@@ -124,6 +131,7 @@ fn add_to_nested_component() {
124131
source,
125132
range,
126133
description,
134+
homepage,
127135
}) => {
128136
assert_eq!(name, &Some("foo".to_owned()));
129137
let producers = producers.as_ref().expect("some producers");
@@ -151,9 +159,16 @@ fn add_to_nested_component() {
151159
Source::new("https://github.com/bytecodealliance/wasm-tools").unwrap()
152160
),
153161
);
162+
assert_eq!(
163+
homepage,
164+
&Some(
165+
Homepage::new("https://github.com/bytecodealliance/wasm-tools")
166+
.unwrap()
167+
),
168+
);
154169

155170
assert_eq!(range.start, 11);
156-
assert_eq!(range.end, 252);
171+
assert_eq!(range.end, 309);
157172
}
158173
_ => panic!("child is a module"),
159174
}

crates/wasm-metadata/tests/module.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ fn add_to_empty_module() {
1717
Licenses::new("Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT").unwrap(),
1818
),
1919
source: Some(Source::new("https://github.com/bytecodealliance/wasm-tools").unwrap()),
20+
homepage: Some(Homepage::new("https://github.com/bytecodealliance/wasm-tools").unwrap()),
2021
};
2122
let module = add.to_wasm(&module).unwrap();
2223

@@ -29,6 +30,7 @@ fn add_to_empty_module() {
2930
source,
3031
range,
3132
description,
33+
homepage,
3234
}) => {
3335
assert_eq!(name, Some("foo".to_owned()));
3436
let producers = producers.expect("some producers");
@@ -51,9 +53,13 @@ fn add_to_empty_module() {
5153
source.unwrap(),
5254
Source::new("https://github.com/bytecodealliance/wasm-tools").unwrap(),
5355
);
56+
assert_eq!(
57+
homepage.unwrap(),
58+
Homepage::new("https://github.com/bytecodealliance/wasm-tools").unwrap(),
59+
);
5460

5561
assert_eq!(range.start, 0);
56-
assert_eq!(range.end, 241);
62+
assert_eq!(range.end, 298);
5763
}
5864
_ => panic!("metadata should be module"),
5965
}

0 commit comments

Comments
 (0)