wasmer_config/package/
package_hash.rs

1use crate::{hash::Sha256Hash, package::PackageParseError};
2
3/// Hash for a package.
4///
5/// Currently only supports the format: `sha256:<hash>`.
6#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
7#[non_exhaustive]
8pub enum PackageHash {
9    Sha256(Sha256Hash),
10}
11
12impl PackageHash {
13    const SHA256_STR_PREFIX: &'static str = "sha256:";
14
15    pub fn as_sha256(&self) -> Option<&Sha256Hash> {
16        match self {
17            PackageHash::Sha256(hash) => Some(hash),
18        }
19    }
20
21    pub fn from_sha256_bytes(bytes: [u8; 32]) -> Self {
22        Self::Sha256(Sha256Hash(bytes))
23    }
24}
25
26impl From<Sha256Hash> for PackageHash {
27    fn from(value: Sha256Hash) -> Self {
28        Self::Sha256(value)
29    }
30}
31
32impl std::fmt::Display for PackageHash {
33    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
34        match self {
35            Self::Sha256(hash) => write!(f, "sha256:{hash}"),
36        }
37    }
38}
39
40impl std::str::FromStr for PackageHash {
41    type Err = PackageParseError;
42
43    fn from_str(s: &str) -> Result<Self, Self::Err> {
44        if !s.starts_with(Self::SHA256_STR_PREFIX) {
45            return Err(PackageParseError::new(
46                s,
47                "package hashes must start with 'sha256:'",
48            ));
49        }
50        let hash = Sha256Hash::from_str(&s[Self::SHA256_STR_PREFIX.len()..])
51            .map_err(|e| PackageParseError::new(s, e.to_string()))?;
52
53        Ok(Self::Sha256(hash))
54    }
55}
56
57impl serde::Serialize for PackageHash {
58    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
59    where
60        S: serde::Serializer,
61    {
62        serializer.serialize_str(&self.to_string())
63    }
64}
65
66impl<'de> serde::Deserialize<'de> for PackageHash {
67    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
68    where
69        D: serde::Deserializer<'de>,
70    {
71        let s = String::deserialize(deserializer)?;
72        s.parse::<Self>()
73            .map_err(|e| serde::de::Error::custom(e.to_string()))
74    }
75}
76
77impl schemars::JsonSchema for PackageHash {
78    fn schema_name() -> String {
79        "PackageHash".to_string()
80    }
81
82    fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema {
83        String::json_schema(gen)
84    }
85}
86
87#[cfg(test)]
88mod tests {
89    use super::*;
90
91    #[test]
92    fn parse_package_hash_roundtrip() {
93        let input = "sha256:c355cd53795b9b481f7eb2b5f4f6c8cf73631bdc343723a579d671e32db70b3c";
94        let h1 = input
95            .parse::<PackageHash>()
96            .expect("string should parse to hash");
97
98        assert_eq!(
99            h1.as_sha256().unwrap().as_bytes(),
100            &[
101                195, 85, 205, 83, 121, 91, 155, 72, 31, 126, 178, 181, 244, 246, 200, 207, 115, 99,
102                27, 220, 52, 55, 35, 165, 121, 214, 113, 227, 45, 183, 11, 60
103            ],
104        );
105
106        assert_eq!(h1.to_string(), input);
107    }
108
109    #[test]
110    fn package_hash_serde_roundtrip() {
111        let input = "sha256:c355cd53795b9b481f7eb2b5f4f6c8cf73631bdc343723a579d671e32db70b3c";
112        let h1 = input
113            .parse::<PackageHash>()
114            .expect("string should parse to hash");
115
116        // Test serialization.
117        assert_eq!(
118            serde_json::to_value(&h1).unwrap(),
119            serde_json::Value::String(input.to_owned()),
120        );
121
122        // Test deserialize.
123        let v = serde_json::to_string(&h1).unwrap();
124        let h2 = serde_json::from_str::<PackageHash>(&v).unwrap();
125
126        assert_eq!(h1, h2);
127    }
128}