Skip to content

Commit

Permalink
[wasm-metadata] add OCI description support (#1936)
Browse files Browse the repository at this point in the history
  • Loading branch information
yoshuawuyts authored Dec 5, 2024
1 parent b2e621d commit bf5854f
Show file tree
Hide file tree
Showing 10 changed files with 159 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, Producers, RegistryMetadata};
use crate::{rewrite_wasm, Author, Description, Producers, RegistryMetadata};

use anyhow::Result;

Expand Down Expand Up @@ -30,6 +30,10 @@ pub struct AddMetadata {
#[cfg_attr(feature = "clap", clap(long, value_name = "NAME"))]
pub author: Option<Author>,

/// A human-readable description of the binary
#[cfg_attr(feature = "clap", clap(long, value_name = "NAME"))]
pub description: Option<Description>,

/// Add an registry metadata to the registry-metadata section
#[cfg_attr(feature="clap", clap(long, value_parser = parse_registry_metadata_value, value_name="PATH"))]
pub registry_metadata: Option<RegistryMetadata>,
Expand Down Expand Up @@ -60,6 +64,7 @@ impl AddMetadata {
&self.name,
&Producers::from_meta(self),
&self.author,
&self.description,
self.registry_metadata.as_ref(),
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;
pub use oci_annotations::{Author, Description};
pub use producers::{Producers, ProducersField};
pub use registry::{CustomLicense, Link, LinkType, RegistryMetadata};

Expand Down
15 changes: 14 additions & 1 deletion crates/wasm-metadata/src/metadata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use std::fmt;
use std::ops::Range;
use wasmparser::{KnownCustom, Parser, Payload::*};

use crate::{Author, ComponentNames, ModuleNames, Producers, RegistryMetadata};
use crate::{Author, ComponentNames, Description, ModuleNames, Producers, RegistryMetadata};

/// A tree of the metadata found in a WebAssembly binary.
#[derive(Debug, Serialize)]
Expand All @@ -20,6 +20,8 @@ pub enum Metadata {
registry_metadata: Option<RegistryMetadata>,
/// The component's author section, if any.
author: Option<Author>,
/// Human-readable description of the binary
description: Option<Description>,
/// All child modules and components inside the component.
children: Vec<Box<Metadata>>,
/// Byte range of the module in the parent binary
Expand All @@ -35,6 +37,8 @@ pub enum Metadata {
registry_metadata: Option<RegistryMetadata>,
/// The component's author section, if any.
author: Option<Author>,
/// Human-readable description of the binary
description: Option<Description>,
/// Byte range of the module in the parent binary
range: Range<usize>,
},
Expand Down Expand Up @@ -117,6 +121,13 @@ impl Metadata {
Metadata::Component { author, .. } => *author = Some(a),
}
}
KnownCustom::Unknown if c.name() == "description" => {
let a = Description::parse_custom_section(&c)?;
match metadata.last_mut().expect("non-empty metadata stack") {
Metadata::Module { description, .. } => *description = Some(a),
Metadata::Component { description, .. } => *description = Some(a),
}
}
_ => {}
},
_ => {}
Expand All @@ -132,6 +143,7 @@ impl Metadata {
name: None,
producers: None,
author: None,
description: None,
registry_metadata: None,
children: Vec::new(),
range,
Expand All @@ -143,6 +155,7 @@ impl Metadata {
name: None,
producers: None,
author: None,
description: None,
registry_metadata: None,
range,
}
Expand Down
110 changes: 110 additions & 0 deletions crates/wasm-metadata/src/oci_annotations/description.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
use std::borrow::Cow;
use std::fmt::{self, Display};
use std::str::FromStr;

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

/// Human-readable description of the binary
#[derive(Debug, Clone, PartialEq)]
pub struct Description(CustomSection<'static>);

impl Description {
/// Create a new instance of `Desrciption`.
pub fn new<S: Into<Cow<'static, str>>>(s: S) -> Self {
Self(CustomSection {
name: "description".into(),
data: match s.into() {
Cow::Borrowed(s) => Cow::Borrowed(s.as_bytes()),
Cow::Owned(s) => Cow::Owned(s.into()),
},
})
}

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

impl FromStr for Description {
type Err = Error;

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

impl Serialize for Description {
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 Description {
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 Description {
fn id(&self) -> u8 {
ComponentSection::id(&self.0)
}
}

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

impl Encode for Description {
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(&Description::new("Nori likes chicken"));
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 = Description::parse_custom_section(&reader).unwrap();
assert_eq!(description.to_string(), "Nori likes chicken");
parsed = true;
}
}
assert!(parsed);
}

#[test]
fn serialize() {
let description = Description::new("Chashu likes tuna");
let json = serde_json::to_string(&description).unwrap();
assert_eq!(r#""Chashu likes tuna""#, 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 @@ -16,5 +16,7 @@
//! [OCI Annotations Spec]: https://specs.opencontainers.org/image-spec/annotations/
pub use author::Author;
pub use description::Description;

mod author;
mod description;
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, input)
rewrite_wasm(&None, self, &None, &None, None, input)
}

pub(crate) fn display(&self, f: &mut fmt::Formatter, indent: usize) -> fmt::Result {
Expand Down
2 changes: 1 addition & 1 deletion crates/wasm-metadata/src/registry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ impl RegistryMetadata {
/// Merge into an existing wasm module. Rewrites the module with this registry-metadata section
/// overwriting its existing one, or adds this registry-metadata section if none is present.
pub fn add_to_wasm(&self, input: &[u8]) -> Result<Vec<u8>> {
rewrite_wasm(&None, &Producers::empty(), &None, Some(&self), input)
rewrite_wasm(&None, &Producers::empty(), &None, &None, Some(&self), input)
}

/// Parse a Wasm binary and extract the `Registry` section, if there is any.
Expand Down
13 changes: 12 additions & 1 deletion crates/wasm-metadata/src/rewrite.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::{Author, ComponentNames, ModuleNames, Producers, RegistryMetadata};
use crate::{Author, ComponentNames, Description, ModuleNames, Producers, RegistryMetadata};
use anyhow::Result;
use std::borrow::Cow;
use std::mem;
Expand All @@ -10,6 +10,7 @@ pub(crate) fn rewrite_wasm(
add_name: &Option<String>,
add_producers: &Producers,
add_author: &Option<Author>,
add_description: &Option<Description>,
add_registry_metadata: Option<&RegistryMetadata>,
input: &[u8],
) -> Result<Vec<u8>> {
Expand Down Expand Up @@ -98,6 +99,13 @@ pub(crate) fn rewrite_wasm(
continue;
}
}
KnownCustom::Unknown if c.name() == "description" => {
if add_description.is_none() {
let description = Description::parse_custom_section(c)?;
description.append_to(&mut output);
continue;
}
}
_ => {}
}
}
Expand Down Expand Up @@ -130,6 +138,9 @@ pub(crate) fn rewrite_wasm(
if let Some(author) = add_author {
author.append_to(&mut output);
}
if let Some(description) = add_description {
description.append_to(&mut output);
}
if add_registry_metadata.is_some() {
let registry_metadata = wasm_encoder::CustomSection {
name: Cow::Borrowed("registry-metadata"),
Expand Down
10 changes: 8 additions & 2 deletions crates/wasm-metadata/tests/component.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ fn add_to_empty_component() {
processed_by: vec![("baz".to_owned(), "1.0".to_owned())],
sdk: vec![],
author: Some(Author::new("Chashu Cat")),
description: Some(Description::new("Chashu likes tuna")),
registry_metadata: Some(RegistryMetadata {
authors: Some(vec!["foo".to_owned()]),
description: Some("foo bar baz".to_owned()),
Expand Down Expand Up @@ -44,6 +45,7 @@ fn add_to_empty_component() {
producers,
registry_metadata,
author,
description,
children,
range,
} => {
Expand All @@ -60,6 +62,7 @@ fn add_to_empty_component() {
);

assert_eq!(author.unwrap(), Author::new("Chashu Cat"));
assert_eq!(description.unwrap(), Description::new("Chashu likes tuna"));

let registry_metadata = registry_metadata.unwrap();

Expand Down Expand Up @@ -103,7 +106,7 @@ fn add_to_empty_component() {
);

assert_eq!(range.start, 0);
assert_eq!(range.end, 454);
assert_eq!(range.end, 485);
}
_ => panic!("metadata should be component"),
}
Expand All @@ -119,6 +122,7 @@ fn add_to_nested_component() {
processed_by: vec![("baz".to_owned(), "1.0".to_owned())],
sdk: vec![],
author: Some(Author::new("Chashu Cat")),
description: Some(Description::new("Chashu likes tuna")),
registry_metadata: Some(RegistryMetadata {
authors: Some(vec!["Foo".to_owned()]),
..Default::default()
Expand Down Expand Up @@ -167,6 +171,7 @@ fn add_to_nested_component() {
author,
registry_metadata,
range,
description,
} => {
assert_eq!(name, &Some("foo".to_owned()));
let producers = producers.as_ref().expect("some producers");
Expand All @@ -180,6 +185,7 @@ fn add_to_nested_component() {
);

assert_eq!(author, &Some(Author::new("Chashu Cat")));
assert_eq!(description, &Some(Description::new("Chashu likes tuna")));

let registry_metadata = registry_metadata.as_ref().unwrap();
assert_eq!(
Expand All @@ -188,7 +194,7 @@ fn add_to_nested_component() {
);

assert_eq!(range.start, 11);
assert_eq!(range.end, 143);
assert_eq!(range.end, 174);
}
_ => panic!("child is a module"),
}
Expand Down
5 changes: 4 additions & 1 deletion crates/wasm-metadata/tests/module.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ fn add_to_empty_module() {
processed_by: vec![("baz".to_owned(), "1.0".to_owned())],
sdk: vec![],
author: Some(Author::new("Chashu Cat")),
description: Some(Description::new("Chashu likes tuna")),
registry_metadata: Some(RegistryMetadata {
authors: Some(vec!["foo".to_owned()]),
description: Some("foo bar baz".to_owned()),
Expand Down Expand Up @@ -43,6 +44,7 @@ fn add_to_empty_module() {
name,
producers,
author,
description,
registry_metadata,
range,
} => {
Expand All @@ -58,6 +60,7 @@ fn add_to_empty_module() {
);

assert_eq!(author.unwrap(), Author::new("Chashu Cat"));
assert_eq!(description.unwrap(), Description::new("Chashu likes tuna"));

let registry_metadata = registry_metadata.unwrap();

Expand Down Expand Up @@ -101,7 +104,7 @@ fn add_to_empty_module() {
);

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

0 comments on commit bf5854f

Please sign in to comment.