wasm_metadata/oci_annotations/
version.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/// Version of the packaged software
11#[derive(Debug, Clone, PartialEq)]
12pub struct Version(CustomSection<'static>);
13
14impl Version {
15    /// Create a new instance of `Desrciption`.
16    pub fn new<S: Into<Cow<'static, str>>>(s: S) -> Self {
17        Self(CustomSection {
18            name: "version".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 `version` custom section from a wasm binary.
27    pub(crate) fn parse_custom_section(reader: &CustomSectionReader<'_>) -> Result<Self> {
28        ensure!(
29            reader.name() == "version",
30            "The `version` custom section should have a name of 'version'"
31        );
32        let data = String::from_utf8(reader.data().to_owned())?;
33        Ok(Self::new(data))
34    }
35}
36
37impl FromStr for Version {
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 Version {
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 Version {
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 Version {
64    fn id(&self) -> u8 {
65        ComponentSection::id(&self.0)
66    }
67}
68
69impl Section for Version {
70    fn id(&self) -> u8 {
71        Section::id(&self.0)
72    }
73}
74
75impl Encode for Version {
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(&Version::new("1.0.0"));
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 version = Version::parse_custom_section(&reader).unwrap();
97                assert_eq!(version.to_string(), "1.0.0");
98                parsed = true;
99            }
100        }
101        assert!(parsed);
102    }
103
104    #[test]
105    fn serialize() {
106        let version = Version::new("1.0.0");
107        let json = serde_json::to_string(&version).unwrap();
108        assert_eq!(r#""1.0.0""#, json);
109    }
110}