ssri/
hash.rs

1use std::cmp::Ordering;
2use std::fmt;
3
4use crate::algorithm::Algorithm;
5use crate::errors::Error;
6
7/**
8Represents a single algorithm/digest pair.
9
10This is mostly internal, although users might interact with it directly on
11occasion.
12*/
13#[derive(Clone, Debug, PartialEq, Eq, Hash)]
14pub struct Hash {
15    pub algorithm: Algorithm,
16    pub digest: String,
17}
18
19impl PartialOrd for Hash {
20    fn partial_cmp(&self, other: &Hash) -> Option<Ordering> {
21        Some(self.cmp(other))
22    }
23}
24
25impl Ord for Hash {
26    fn cmp(&self, other: &Hash) -> Ordering {
27        self.algorithm.cmp(&other.algorithm)
28    }
29}
30impl fmt::Display for Hash {
31    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
32        write!(f, "{}-{}", self.algorithm, self.digest)
33    }
34}
35
36impl std::str::FromStr for Hash {
37    type Err = Error;
38
39    /// Tries to parse a [&str] into a [struct@Hash].
40    /// Note the length of the digest is not validated to encode the number of
41    /// bytes expected by the chosen hash algorithm.
42    fn from_str(s: &str) -> Result<Hash, Self::Err> {
43        let mut parsed = s.trim().split(|c| c == '-');
44        let algorithm = parsed
45            .next()
46            .ok_or_else(|| Error::ParseIntegrityError(s.into()))?
47            .parse()?;
48        let digest = String::from(
49            parsed
50                .next()
51                .ok_or_else(|| Error::ParseIntegrityError(s.into()))?,
52        );
53        Ok(Hash { algorithm, digest })
54    }
55}
56
57#[cfg(test)]
58mod tests {
59    use super::Algorithm;
60    use super::Hash;
61
62    #[test]
63    fn hash_stringify() {
64        assert_eq!(
65            format!(
66                "{}",
67                Hash {
68                    algorithm: Algorithm::Sha256,
69                    digest: String::from("deadbeef==")
70                }
71            ),
72            "sha256-deadbeef=="
73        )
74    }
75
76    #[test]
77    fn parsing() {
78        assert_eq!(
79            " sha256-deadbeef== \n".parse::<Hash>().unwrap(),
80            Hash {
81                algorithm: Algorithm::Sha256,
82                digest: String::from("deadbeef==")
83            }
84        )
85    }
86
87    #[test]
88    #[should_panic]
89    fn bad_algorithm() {
90        // TODO - test the actual error returned when it's more valuable
91        "sha7-deadbeef==".parse::<Hash>().unwrap();
92    }
93
94    #[test]
95    fn ordering() {
96        let mut arr = [
97            Hash {
98                algorithm: Algorithm::Sha1,
99                digest: String::from("foo=="),
100            },
101            Hash {
102                algorithm: Algorithm::Sha256,
103                digest: String::from("foo=="),
104            },
105            Hash {
106                algorithm: Algorithm::Sha384,
107                digest: String::from("foo=="),
108            },
109            Hash {
110                algorithm: Algorithm::Sha512,
111                digest: String::from("foo=="),
112            },
113            Hash {
114                algorithm: Algorithm::Xxh3,
115                digest: String::from("foo=="),
116            },
117        ];
118        arr.sort_unstable();
119        assert_eq!(
120            arr,
121            [
122                Hash {
123                    algorithm: Algorithm::Sha512,
124                    digest: String::from("foo==")
125                },
126                Hash {
127                    algorithm: Algorithm::Sha384,
128                    digest: String::from("foo==")
129                },
130                Hash {
131                    algorithm: Algorithm::Sha256,
132                    digest: String::from("foo==")
133                },
134                Hash {
135                    algorithm: Algorithm::Sha1,
136                    digest: String::from("foo==")
137                },
138                Hash {
139                    algorithm: Algorithm::Xxh3,
140                    digest: String::from("foo==")
141                }
142            ]
143        )
144    }
145}