Skip to content

Commit

Permalink
[wasm-metadata] add support for Homepage custom section (bytecodealli…
Browse files Browse the repository at this point in the history
  • Loading branch information
yoshuawuyts authored Dec 10, 2024
1 parent 2efb389 commit 1bcecbc
Show file tree
Hide file tree
Showing 10 changed files with 179 additions and 9 deletions.
7 changes: 6 additions & 1 deletion crates/wasm-metadata/src/add_metadata.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::{rewrite_wasm, Author, Description, Licenses, Producers, Source};
use crate::{rewrite_wasm, Author, Description, Homepage, Licenses, Producers, Source};

use anyhow::Result;

Expand Down Expand Up @@ -41,6 +41,10 @@ pub struct AddMetadata {
/// URL to get source code for building the image
#[cfg_attr(feature = "clap", clap(long, value_name = "NAME"))]
pub source: Option<Source>,

/// URL to find more information on the binary
#[cfg_attr(feature = "clap", clap(long, value_name = "NAME"))]
pub homepage: Option<Homepage>,
}

#[cfg(feature = "clap")]
Expand All @@ -62,6 +66,7 @@ impl AddMetadata {
&self.description,
&self.licenses,
&self.source,
&self.homepage,
input,
)
}
Expand Down
2 changes: 1 addition & 1 deletion crates/wasm-metadata/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
pub use add_metadata::AddMetadata;
pub use metadata::Metadata;
pub use names::{ComponentNames, ModuleNames};
pub use oci_annotations::{Author, Description, Licenses, Source};
pub use oci_annotations::{Author, Description, Homepage, Licenses, Source};
pub use payload::Payload;
pub use producers::{Producers, ProducersField};

Expand Down
4 changes: 3 additions & 1 deletion crates/wasm-metadata/src/metadata.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use serde_derive::Serialize;
use std::ops::Range;

use crate::{Author, Description, Licenses, Producers, Source};
use crate::{Author, Description, Homepage, Licenses, Producers, Source};

/// Metadata associated with a Wasm Component or Module
#[derive(Debug, Serialize, Default)]
Expand All @@ -19,6 +19,8 @@ pub struct Metadata {
pub licenses: Option<Licenses>,
/// URL to get source code for building the image
pub source: Option<Source>,
/// URL to find more information on the binary
pub homepage: Option<Homepage>,
/// Byte range of the module in the parent binary
pub range: Range<usize>,
}
118 changes: 118 additions & 0 deletions crates/wasm-metadata/src/oci_annotations/homepage.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
use std::borrow::Cow;
use std::fmt::{self, Display};
use std::str::FromStr;

use anyhow::{ensure, Error, Result};
use serde::Serialize;
use url::Url;
use wasm_encoder::{ComponentSection, CustomSection, Encode, Section};
use wasmparser::CustomSectionReader;

/// URL to find more information on the binary
#[derive(Debug, Clone, PartialEq)]
pub struct Homepage(CustomSection<'static>);

impl Homepage {
/// Create a new instance of `Homepage`.
pub fn new(s: &str) -> Result<Self> {
Ok(Url::parse(s)?.into())
}

/// Parse a `homepage` custom section from a wasm binary.
pub(crate) fn parse_custom_section(reader: &CustomSectionReader<'_>) -> Result<Self> {
ensure!(
reader.name() == "homepage",
"The `homepage` custom section should have a name of 'homepage'"
);
let data = String::from_utf8(reader.data().to_owned())?;
Self::new(&data)
}
}

impl FromStr for Homepage {
type Err = Error;

fn from_str(s: &str) -> Result<Self, Self::Err> {
Self::new(s)
}
}

impl From<Url> for Homepage {
fn from(expression: Url) -> Self {
Self(CustomSection {
name: "homepage".into(),
data: Cow::Owned(expression.to_string().into_bytes()),
})
}
}

impl Serialize for Homepage {
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serializer.serialize_str(&self.to_string())
}
}

impl Display for Homepage {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
// NOTE: this will never panic since we always guarantee the data is
// encoded as utf8, even if we internally store it as [u8].
let data = String::from_utf8(self.0.data.to_vec()).unwrap();
write!(f, "{data}")
}
}

impl ComponentSection for Homepage {
fn id(&self) -> u8 {
ComponentSection::id(&self.0)
}
}

impl Section for Homepage {
fn id(&self) -> u8 {
Section::id(&self.0)
}
}

impl Encode for Homepage {
fn encode(&self, sink: &mut Vec<u8>) {
self.0.encode(sink);
}
}

#[cfg(test)]
mod test {
use super::*;
use wasm_encoder::Component;
use wasmparser::Payload;

#[test]
fn roundtrip() {
let mut component = Component::new();
component
.section(&Homepage::new("https://github.com/bytecodealliance/wasm-tools").unwrap());
let component = component.finish();

let mut parsed = false;
for section in wasmparser::Parser::new(0).parse_all(&component) {
if let Payload::CustomSection(reader) = section.unwrap() {
let description = Homepage::parse_custom_section(&reader).unwrap();
assert_eq!(
description.to_string(),
"https://github.com/bytecodealliance/wasm-tools"
);
parsed = true;
}
}
assert!(parsed);
}

#[test]
fn serialize() {
let description = Homepage::new("https://github.com/bytecodealliance/wasm-tools").unwrap();
let json = serde_json::to_string(&description).unwrap();
assert_eq!(r#""https://github.com/bytecodealliance/wasm-tools""#, json);
}
}
2 changes: 2 additions & 0 deletions crates/wasm-metadata/src/oci_annotations/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,12 @@
pub use author::Author;
pub use description::Description;
pub use homepage::Homepage;
pub use licenses::Licenses;
pub use source::Source;

mod author;
mod description;
mod homepage;
mod licenses;
mod source;
11 changes: 10 additions & 1 deletion crates/wasm-metadata/src/payload.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ use serde_derive::Serialize;
use wasmparser::{KnownCustom, Parser, Payload::*};

use crate::{
Author, ComponentNames, Description, Licenses, Metadata, ModuleNames, Producers, Source,
Author, ComponentNames, Description, Homepage, Licenses, Metadata, ModuleNames, Producers,
Source,
};

/// Data representing either a Wasm Component or module
Expand Down Expand Up @@ -125,6 +126,14 @@ impl Payload {
.metadata_mut();
*source = Some(a);
}
KnownCustom::Unknown if c.name() == "homepage" => {
let a = Homepage::parse_custom_section(&c)?;
let Metadata { homepage, .. } = output
.last_mut()
.expect("non-empty metadata stack")
.metadata_mut();
*homepage = Some(a);
}
_ => {}
},
_ => {}
Expand Down
2 changes: 1 addition & 1 deletion crates/wasm-metadata/src/producers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ impl Producers {
/// Merge into an existing wasm module. Rewrites the module with this producers section
/// merged into its existing one, or adds this producers section if none is present.
pub fn add_to_wasm(&self, input: &[u8]) -> Result<Vec<u8>> {
rewrite_wasm(&None, self, &None, &None, &None, &None, input)
rewrite_wasm(&None, self, &None, &None, &None, &None, &None, input)
}

pub(crate) fn display(&self, f: &mut fmt::Formatter, indent: usize) -> fmt::Result {
Expand Down
15 changes: 14 additions & 1 deletion crates/wasm-metadata/src/rewrite.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
use crate::{Author, ComponentNames, Description, Licenses, ModuleNames, Producers, Source};
use crate::{
Author, ComponentNames, Description, Homepage, Licenses, ModuleNames, Producers, Source,
};
use anyhow::Result;
use std::mem;
use wasm_encoder::ComponentSection as _;
Expand All @@ -12,6 +14,7 @@ pub(crate) fn rewrite_wasm(
add_description: &Option<Description>,
add_licenses: &Option<Licenses>,
add_source: &Option<Source>,
add_homepage: &Option<Homepage>,
input: &[u8],
) -> Result<Vec<u8>> {
let mut producers_found = false;
Expand Down Expand Up @@ -106,6 +109,13 @@ pub(crate) fn rewrite_wasm(
continue;
}
}
KnownCustom::Unknown if c.name() == "homepage" => {
if add_source.is_none() {
let homepage = Homepage::parse_custom_section(c)?;
homepage.append_to(&mut output);
continue;
}
}
_ => {}
}
}
Expand Down Expand Up @@ -147,5 +157,8 @@ pub(crate) fn rewrite_wasm(
if let Some(source) = add_source {
source.append_to(&mut output);
}
if let Some(homepage) = add_homepage {
homepage.append_to(&mut output);
}
Ok(output)
}
19 changes: 17 additions & 2 deletions crates/wasm-metadata/tests/component.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ fn add_to_empty_component() {
Licenses::new("Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT").unwrap(),
),
source: Some(Source::new("https://github.com/bytecodealliance/wasm-tools").unwrap()),
homepage: Some(Homepage::new("https://github.com/bytecodealliance/wasm-tools").unwrap()),
};
let component = add.to_wasm(&component).unwrap();

Expand All @@ -32,6 +33,7 @@ fn add_to_empty_component() {
licenses,
source,
range,
homepage,
},
} => {
assert!(children.is_empty());
Expand All @@ -56,9 +58,13 @@ fn add_to_empty_component() {
source.unwrap(),
Source::new("https://github.com/bytecodealliance/wasm-tools").unwrap(),
);
assert_eq!(
homepage.unwrap(),
Homepage::new("https://github.com/bytecodealliance/wasm-tools").unwrap(),
);

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

Expand Down Expand Up @@ -124,6 +131,7 @@ fn add_to_nested_component() {
source,
range,
description,
homepage,
}) => {
assert_eq!(name, &Some("foo".to_owned()));
let producers = producers.as_ref().expect("some producers");
Expand Down Expand Up @@ -151,9 +159,16 @@ fn add_to_nested_component() {
Source::new("https://github.com/bytecodealliance/wasm-tools").unwrap()
),
);
assert_eq!(
homepage,
&Some(
Homepage::new("https://github.com/bytecodealliance/wasm-tools")
.unwrap()
),
);

assert_eq!(range.start, 11);
assert_eq!(range.end, 252);
assert_eq!(range.end, 309);
}
_ => panic!("child is a module"),
}
Expand Down
8 changes: 7 additions & 1 deletion crates/wasm-metadata/tests/module.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ fn add_to_empty_module() {
Licenses::new("Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT").unwrap(),
),
source: Some(Source::new("https://github.com/bytecodealliance/wasm-tools").unwrap()),
homepage: Some(Homepage::new("https://github.com/bytecodealliance/wasm-tools").unwrap()),
};
let module = add.to_wasm(&module).unwrap();

Expand All @@ -29,6 +30,7 @@ fn add_to_empty_module() {
source,
range,
description,
homepage,
}) => {
assert_eq!(name, Some("foo".to_owned()));
let producers = producers.expect("some producers");
Expand All @@ -51,9 +53,13 @@ fn add_to_empty_module() {
source.unwrap(),
Source::new("https://github.com/bytecodealliance/wasm-tools").unwrap(),
);
assert_eq!(
homepage.unwrap(),
Homepage::new("https://github.com/bytecodealliance/wasm-tools").unwrap(),
);

assert_eq!(range.start, 0);
assert_eq!(range.end, 241);
assert_eq!(range.end, 298);
}
_ => panic!("metadata should be module"),
}
Expand Down

0 comments on commit 1bcecbc

Please sign in to comment.