wasm_metadata/oci_annotations/
description.rs

1use std::borrow::Cow;
2use std::fmt::{self, Display};
3use std::str::FromStr;
4
5use anyhow::{ensure, Error, Result};
6use serde::Serialize;
7use wasm_encoder::{ComponentSection, CustomSection, Encode, Section};
8use wasmparser::CustomSectionReader;
9
10/// Human-readable description of the binary
11#[derive(Debug, Clone, PartialEq)]
12pub struct Description(CustomSection<'static>);
13
14impl 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
37impl 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
45impl 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
54impl 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
63impl ComponentSection for Description {
64    fn id(&self) -> u8 {
65        ComponentSection::id(&self.0)
66    }
67}
68
69impl Section for Description {
70    fn id(&self) -> u8 {
71        Section::id(&self.0)
72    }
73}
74
75impl Encode for Description {
76    fn encode(&self, sink: &mut Vec<u8>) {
77        self.0.encode(sink);
78    }
79}
80
81#[cfg(test)]
82mod 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}