wasmer_config/package/
package_ident.rs

1use std::{borrow::Cow, str::FromStr};
2
3use super::{NamedPackageIdent, PackageHash, PackageId, PackageParseError};
4
5#[derive(PartialEq, Eq, Clone, Debug, Hash)]
6pub enum PackageIdent {
7    Named(NamedPackageIdent),
8    Hash(PackageHash),
9}
10
11impl PackageIdent {
12    pub fn as_named(&self) -> Option<&NamedPackageIdent> {
13        if let Self::Named(v) = self {
14            Some(v)
15        } else {
16            None
17        }
18    }
19
20    pub fn as_hash(&self) -> Option<&PackageHash> {
21        if let Self::Hash(v) = self {
22            Some(v)
23        } else {
24            None
25        }
26    }
27
28    /// Returns true if this ident matches the given package id.
29    ///
30    /// Semver constraints are matched against the package id's version.
31    pub fn matches_id(&self, id: &PackageId) -> bool {
32        match (self, id) {
33            (Self::Named(a), PackageId::Named(b)) => a.matches_id(b),
34            (Self::Hash(a), PackageId::Hash(b)) => a == b,
35            _ => false,
36        }
37    }
38}
39
40impl From<NamedPackageIdent> for PackageIdent {
41    fn from(value: NamedPackageIdent) -> Self {
42        Self::Named(value)
43    }
44}
45
46impl From<PackageHash> for PackageIdent {
47    fn from(value: PackageHash) -> Self {
48        Self::Hash(value)
49    }
50}
51
52impl std::str::FromStr for PackageIdent {
53    type Err = PackageParseError;
54
55    fn from_str(s: &str) -> Result<Self, Self::Err> {
56        if let Ok(hash) = PackageHash::from_str(s) {
57            Ok(Self::Hash(hash))
58        } else {
59            match NamedPackageIdent::from_str(s) {
60                Ok(named) => Ok(Self::Named(named)),
61                // Surface the specific named-ident parse error rather than a generic message:
62                // the value was not a hash, so the named-ident failure is the actionable reason.
63                Err(e) => Err(e),
64            }
65        }
66    }
67}
68
69impl std::fmt::Display for PackageIdent {
70    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
71        match self {
72            Self::Named(n) => n.fmt(f),
73            Self::Hash(h) => h.fmt(f),
74        }
75    }
76}
77
78impl serde::Serialize for PackageIdent {
79    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
80    where
81        S: serde::ser::Serializer,
82    {
83        self.to_string().serialize(serializer)
84    }
85}
86
87impl<'de> serde::Deserialize<'de> for PackageIdent {
88    fn deserialize<D>(deserializer: D) -> Result<PackageIdent, D::Error>
89    where
90        D: serde::de::Deserializer<'de>,
91    {
92        let s = String::deserialize(deserializer)?;
93        Self::from_str(&s).map_err(serde::de::Error::custom)
94    }
95}
96
97impl schemars::JsonSchema for PackageIdent {
98    fn schema_name() -> Cow<'static, str> {
99        Cow::Borrowed("PackageIdent")
100    }
101
102    fn json_schema(generator: &mut schemars::SchemaGenerator) -> schemars::Schema {
103        String::json_schema(generator)
104    }
105
106    fn inline_schema() -> bool {
107        false
108    }
109
110    fn schema_id() -> std::borrow::Cow<'static, str> {
111        Self::schema_name()
112    }
113}
114
115#[cfg(test)]
116mod tests {
117    use super::*;
118
119    #[test]
120    fn test_package_ident_matches_id() {
121        assert!(
122            PackageIdent::from_str("ns/pkg")
123                .unwrap()
124                .matches_id(&PackageId::new_named("ns/pkg", "1.0.0".parse().unwrap()))
125        );
126
127        assert!(
128            PackageIdent::from_str("ns/pkg@2")
129                .unwrap()
130                .matches_id(&PackageId::new_named("ns/pkg", "2.3.7".parse().unwrap()))
131        );
132
133        assert!(
134            !PackageIdent::from_str("ns/pkg@3")
135                .unwrap()
136                .matches_id(&PackageId::new_named("ns/pkg", "2.3.7".parse().unwrap()))
137        );
138    }
139}