wasm_metadata/oci_annotations/
homepage.rs

1use std::borrow::Cow;
2use std::fmt::{self, Display};
3use std::str::FromStr;
4
5use anyhow::{ensure, Error, Result};
6use serde::Serialize;
7use url::Url;
8use wasm_encoder::{ComponentSection, CustomSection, Encode, Section};
9use wasmparser::CustomSectionReader;
10
11/// URL to find more information on the binary
12#[derive(Debug, Clone, PartialEq)]
13pub struct Homepage(CustomSection<'static>);
14
15impl Homepage {
16    /// Create a new instance of `Homepage`.
17    pub fn new(s: &str) -> Result<Self> {
18        Ok(Url::parse(s)?.into())
19    }
20
21    /// Parse a `homepage` custom section from a wasm binary.
22    pub(crate) fn parse_custom_section(reader: &CustomSectionReader<'_>) -> Result<Self> {
23        ensure!(
24            reader.name() == "homepage",
25            "The `homepage` custom section should have a name of 'homepage'"
26        );
27        let data = String::from_utf8(reader.data().to_owned())?;
28        Self::new(&data)
29    }
30}
31
32impl FromStr for Homepage {
33    type Err = Error;
34
35    fn from_str(s: &str) -> Result<Self, Self::Err> {
36        Self::new(s)
37    }
38}
39
40impl From<Url> for Homepage {
41    fn from(expression: Url) -> Self {
42        Self(CustomSection {
43            name: "homepage".into(),
44            data: Cow::Owned(expression.to_string().into_bytes()),
45        })
46    }
47}
48
49impl Serialize for Homepage {
50    fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
51    where
52        S: serde::Serializer,
53    {
54        serializer.serialize_str(&self.to_string())
55    }
56}
57
58impl Display for Homepage {
59    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
60        // NOTE: this will never panic since we always guarantee the data is
61        // encoded as utf8, even if we internally store it as [u8].
62        let data = String::from_utf8(self.0.data.to_vec()).unwrap();
63        write!(f, "{data}")
64    }
65}
66
67impl ComponentSection for Homepage {
68    fn id(&self) -> u8 {
69        ComponentSection::id(&self.0)
70    }
71}
72
73impl Section for Homepage {
74    fn id(&self) -> u8 {
75        Section::id(&self.0)
76    }
77}
78
79impl Encode for Homepage {
80    fn encode(&self, sink: &mut Vec<u8>) {
81        self.0.encode(sink);
82    }
83}
84
85#[cfg(test)]
86mod test {
87    use super::*;
88    use wasm_encoder::Component;
89    use wasmparser::Payload;
90
91    #[test]
92    fn roundtrip() {
93        let mut component = Component::new();
94        component
95            .section(&Homepage::new("https://github.com/bytecodealliance/wasm-tools").unwrap());
96        let component = component.finish();
97
98        let mut parsed = false;
99        for section in wasmparser::Parser::new(0).parse_all(&component) {
100            if let Payload::CustomSection(reader) = section.unwrap() {
101                let description = Homepage::parse_custom_section(&reader).unwrap();
102                assert_eq!(
103                    description.to_string(),
104                    "https://github.com/bytecodealliance/wasm-tools"
105                );
106                parsed = true;
107            }
108        }
109        assert!(parsed);
110    }
111
112    #[test]
113    fn serialize() {
114        let description = Homepage::new("https://github.com/bytecodealliance/wasm-tools").unwrap();
115        let json = serde_json::to_string(&description).unwrap();
116        assert_eq!(r#""https://github.com/bytecodealliance/wasm-tools""#, json);
117    }
118}