wasmer_config/package/
package_hash.rs

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