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