Skip to content

Commit bf5854f

Browse files
authored
[wasm-metadata] add OCI description support (#1936)
1 parent b2e621d commit bf5854f

File tree

10 files changed

+159
-9
lines changed

10 files changed

+159
-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, Producers, RegistryMetadata};
1+
use crate::{rewrite_wasm, Author, Description, Producers, RegistryMetadata};
22

33
use anyhow::Result;
44

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

33+
/// A human-readable description of the binary
34+
#[cfg_attr(feature = "clap", clap(long, value_name = "NAME"))]
35+
pub description: Option<Description>,
36+
3337
/// Add an registry metadata to the registry-metadata section
3438
#[cfg_attr(feature="clap", clap(long, value_parser = parse_registry_metadata_value, value_name="PATH"))]
3539
pub registry_metadata: Option<RegistryMetadata>,
@@ -60,6 +64,7 @@ impl AddMetadata {
6064
&self.name,
6165
&Producers::from_meta(self),
6266
&self.author,
67+
&self.description,
6368
self.registry_metadata.as_ref(),
6469
input,
6570
)

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;
8+
pub use oci_annotations::{Author, Description};
99
pub use producers::{Producers, ProducersField};
1010
pub use registry::{CustomLicense, Link, LinkType, RegistryMetadata};
1111

crates/wasm-metadata/src/metadata.rs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use std::fmt;
44
use std::ops::Range;
55
use wasmparser::{KnownCustom, Parser, Payload::*};
66

7-
use crate::{Author, ComponentNames, ModuleNames, Producers, RegistryMetadata};
7+
use crate::{Author, ComponentNames, Description, ModuleNames, Producers, RegistryMetadata};
88

99
/// A tree of the metadata found in a WebAssembly binary.
1010
#[derive(Debug, Serialize)]
@@ -20,6 +20,8 @@ pub enum Metadata {
2020
registry_metadata: Option<RegistryMetadata>,
2121
/// The component's author section, if any.
2222
author: Option<Author>,
23+
/// Human-readable description of the binary
24+
description: Option<Description>,
2325
/// All child modules and components inside the component.
2426
children: Vec<Box<Metadata>>,
2527
/// Byte range of the module in the parent binary
@@ -35,6 +37,8 @@ pub enum Metadata {
3537
registry_metadata: Option<RegistryMetadata>,
3638
/// The component's author section, if any.
3739
author: Option<Author>,
40+
/// Human-readable description of the binary
41+
description: Option<Description>,
3842
/// Byte range of the module in the parent binary
3943
range: Range<usize>,
4044
},
@@ -117,6 +121,13 @@ impl Metadata {
117121
Metadata::Component { author, .. } => *author = Some(a),
118122
}
119123
}
124+
KnownCustom::Unknown if c.name() == "description" => {
125+
let a = Description::parse_custom_section(&c)?;
126+
match metadata.last_mut().expect("non-empty metadata stack") {
127+
Metadata::Module { description, .. } => *description = Some(a),
128+
Metadata::Component { description, .. } => *description = Some(a),
129+
}
130+
}
120131
_ => {}
121132
},
122133
_ => {}
@@ -132,6 +143,7 @@ impl Metadata {
132143
name: None,
133144
producers: None,
134145
author: None,
146+
description: None,
135147
registry_metadata: None,
136148
children: Vec::new(),
137149
range,
@@ -143,6 +155,7 @@ impl Metadata {
143155
name: None,
144156
producers: None,
145157
author: None,
158+
description: None,
146159
registry_metadata: None,
147160
range,
148161
}
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
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 wasm_encoder::{ComponentSection, CustomSection, Encode, Section};
8+
use wasmparser::CustomSectionReader;
9+
10+
/// Human-readable description of the binary
11+
#[derive(Debug, Clone, PartialEq)]
12+
pub struct Description(CustomSection<'static>);
13+
14+
impl Description {
15+
/// Create a new instance of `Desrciption`.
16+
pub fn new<S: Into<Cow<'static, str>>>(s: S) -> Self {
17+
Self(CustomSection {
18+
name: "description".into(),
19+
data: match s.into() {
20+
Cow::Borrowed(s) => Cow::Borrowed(s.as_bytes()),
21+
Cow::Owned(s) => Cow::Owned(s.into()),
22+
},
23+
})
24+
}
25+
26+
/// Parse an `description` custom section from a wasm binary.
27+
pub(crate) fn parse_custom_section(reader: &CustomSectionReader<'_>) -> Result<Self> {
28+
ensure!(
29+
reader.name() == "description",
30+
"The `description` custom section should have a name of 'description'"
31+
);
32+
let data = String::from_utf8(reader.data().to_owned())?;
33+
Ok(Self::new(data))
34+
}
35+
}
36+
37+
impl FromStr for Description {
38+
type Err = Error;
39+
40+
fn from_str(s: &str) -> Result<Self, Self::Err> {
41+
Ok(Self::new(s.to_owned()))
42+
}
43+
}
44+
45+
impl Serialize for Description {
46+
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
47+
where
48+
S: serde::Serializer,
49+
{
50+
serializer.serialize_str(&self.to_string())
51+
}
52+
}
53+
54+
impl Display for Description {
55+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
56+
// NOTE: this will never panic since we always guarantee the data is
57+
// encoded as utf8, even if we internally store it as [u8].
58+
let data = String::from_utf8(self.0.data.to_vec()).unwrap();
59+
write!(f, "{data}")
60+
}
61+
}
62+
63+
impl ComponentSection for Description {
64+
fn id(&self) -> u8 {
65+
ComponentSection::id(&self.0)
66+
}
67+
}
68+
69+
impl Section for Description {
70+
fn id(&self) -> u8 {
71+
Section::id(&self.0)
72+
}
73+
}
74+
75+
impl Encode for Description {
76+
fn encode(&self, sink: &mut Vec<u8>) {
77+
self.0.encode(sink);
78+
}
79+
}
80+
81+
#[cfg(test)]
82+
mod test {
83+
use super::*;
84+
use wasm_encoder::Component;
85+
use wasmparser::Payload;
86+
87+
#[test]
88+
fn roundtrip() {
89+
let mut component = Component::new();
90+
component.section(&Description::new("Nori likes chicken"));
91+
let component = component.finish();
92+
93+
let mut parsed = false;
94+
for section in wasmparser::Parser::new(0).parse_all(&component) {
95+
if let Payload::CustomSection(reader) = section.unwrap() {
96+
let description = Description::parse_custom_section(&reader).unwrap();
97+
assert_eq!(description.to_string(), "Nori likes chicken");
98+
parsed = true;
99+
}
100+
}
101+
assert!(parsed);
102+
}
103+
104+
#[test]
105+
fn serialize() {
106+
let description = Description::new("Chashu likes tuna");
107+
let json = serde_json::to_string(&description).unwrap();
108+
assert_eq!(r#""Chashu likes tuna""#, json);
109+
}
110+
}

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,5 +16,7 @@
1616
//! [OCI Annotations Spec]: https://specs.opencontainers.org/image-spec/annotations/
1717
1818
pub use author::Author;
19+
pub use description::Description;
1920

2021
mod author;
22+
mod description;

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, input)
151+
rewrite_wasm(&None, self, &None, &None, None, input)
152152
}
153153

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

crates/wasm-metadata/src/registry.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ impl RegistryMetadata {
4444
/// Merge into an existing wasm module. Rewrites the module with this registry-metadata section
4545
/// overwriting its existing one, or adds this registry-metadata section if none is present.
4646
pub fn add_to_wasm(&self, input: &[u8]) -> Result<Vec<u8>> {
47-
rewrite_wasm(&None, &Producers::empty(), &None, Some(&self), input)
47+
rewrite_wasm(&None, &Producers::empty(), &None, &None, Some(&self), input)
4848
}
4949

5050
/// Parse a Wasm binary and extract the `Registry` section, if there is any.

crates/wasm-metadata/src/rewrite.rs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use crate::{Author, ComponentNames, ModuleNames, Producers, RegistryMetadata};
1+
use crate::{Author, ComponentNames, Description, ModuleNames, Producers, RegistryMetadata};
22
use anyhow::Result;
33
use std::borrow::Cow;
44
use std::mem;
@@ -10,6 +10,7 @@ pub(crate) fn rewrite_wasm(
1010
add_name: &Option<String>,
1111
add_producers: &Producers,
1212
add_author: &Option<Author>,
13+
add_description: &Option<Description>,
1314
add_registry_metadata: Option<&RegistryMetadata>,
1415
input: &[u8],
1516
) -> Result<Vec<u8>> {
@@ -98,6 +99,13 @@ pub(crate) fn rewrite_wasm(
9899
continue;
99100
}
100101
}
102+
KnownCustom::Unknown if c.name() == "description" => {
103+
if add_description.is_none() {
104+
let description = Description::parse_custom_section(c)?;
105+
description.append_to(&mut output);
106+
continue;
107+
}
108+
}
101109
_ => {}
102110
}
103111
}
@@ -130,6 +138,9 @@ pub(crate) fn rewrite_wasm(
130138
if let Some(author) = add_author {
131139
author.append_to(&mut output);
132140
}
141+
if let Some(description) = add_description {
142+
description.append_to(&mut output);
143+
}
133144
if add_registry_metadata.is_some() {
134145
let registry_metadata = wasm_encoder::CustomSection {
135146
name: Cow::Borrowed("registry-metadata"),

crates/wasm-metadata/tests/component.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ fn add_to_empty_component() {
1212
processed_by: vec![("baz".to_owned(), "1.0".to_owned())],
1313
sdk: vec![],
1414
author: Some(Author::new("Chashu Cat")),
15+
description: Some(Description::new("Chashu likes tuna")),
1516
registry_metadata: Some(RegistryMetadata {
1617
authors: Some(vec!["foo".to_owned()]),
1718
description: Some("foo bar baz".to_owned()),
@@ -44,6 +45,7 @@ fn add_to_empty_component() {
4445
producers,
4546
registry_metadata,
4647
author,
48+
description,
4749
children,
4850
range,
4951
} => {
@@ -60,6 +62,7 @@ fn add_to_empty_component() {
6062
);
6163

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

6467
let registry_metadata = registry_metadata.unwrap();
6568

@@ -103,7 +106,7 @@ fn add_to_empty_component() {
103106
);
104107

105108
assert_eq!(range.start, 0);
106-
assert_eq!(range.end, 454);
109+
assert_eq!(range.end, 485);
107110
}
108111
_ => panic!("metadata should be component"),
109112
}
@@ -119,6 +122,7 @@ fn add_to_nested_component() {
119122
processed_by: vec![("baz".to_owned(), "1.0".to_owned())],
120123
sdk: vec![],
121124
author: Some(Author::new("Chashu Cat")),
125+
description: Some(Description::new("Chashu likes tuna")),
122126
registry_metadata: Some(RegistryMetadata {
123127
authors: Some(vec!["Foo".to_owned()]),
124128
..Default::default()
@@ -167,6 +171,7 @@ fn add_to_nested_component() {
167171
author,
168172
registry_metadata,
169173
range,
174+
description,
170175
} => {
171176
assert_eq!(name, &Some("foo".to_owned()));
172177
let producers = producers.as_ref().expect("some producers");
@@ -180,6 +185,7 @@ fn add_to_nested_component() {
180185
);
181186

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

184190
let registry_metadata = registry_metadata.as_ref().unwrap();
185191
assert_eq!(
@@ -188,7 +194,7 @@ fn add_to_nested_component() {
188194
);
189195

190196
assert_eq!(range.start, 11);
191-
assert_eq!(range.end, 143);
197+
assert_eq!(range.end, 174);
192198
}
193199
_ => panic!("child is a module"),
194200
}

crates/wasm-metadata/tests/module.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ fn add_to_empty_module() {
1212
processed_by: vec![("baz".to_owned(), "1.0".to_owned())],
1313
sdk: vec![],
1414
author: Some(Author::new("Chashu Cat")),
15+
description: Some(Description::new("Chashu likes tuna")),
1516
registry_metadata: Some(RegistryMetadata {
1617
authors: Some(vec!["foo".to_owned()]),
1718
description: Some("foo bar baz".to_owned()),
@@ -43,6 +44,7 @@ fn add_to_empty_module() {
4344
name,
4445
producers,
4546
author,
47+
description,
4648
registry_metadata,
4749
range,
4850
} => {
@@ -58,6 +60,7 @@ fn add_to_empty_module() {
5860
);
5961

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

6265
let registry_metadata = registry_metadata.unwrap();
6366

@@ -101,7 +104,7 @@ fn add_to_empty_module() {
101104
);
102105

103106
assert_eq!(range.start, 0);
104-
assert_eq!(range.end, 444);
107+
assert_eq!(range.end, 475);
105108
}
106109
_ => panic!("metadata should be module"),
107110
}

0 commit comments

Comments
 (0)