Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[wasm-metadata] add OCI description support #1936

Merged
merged 1 commit into from
Dec 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading