1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
use super::{Digest, HashAlgorithm, Sha256};
use anyhow::Error;
use serde::{Deserialize, Serialize};
use std::{fmt, ops::Deref, str::FromStr};
use thiserror::Error;

pub enum Hasher {
    Sha256(Sha256),
}

impl Hasher {
    pub fn update(&mut self, bytes: &[u8]) {
        match self {
            Self::Sha256(d) => d.update(bytes),
        }
    }

    pub fn finalize(self) -> AnyHash {
        let (algo, bytes) = match self {
            Self::Sha256(d) => (HashAlgorithm::Sha256, d.finalize().deref().into()),
        };

        AnyHash { algo, bytes }
    }
}

impl HashAlgorithm {
    pub fn hasher(&self) -> Hasher {
        match self {
            HashAlgorithm::Sha256 => Hasher::Sha256(Sha256::new()),
        }
    }

    pub fn digest(&self, content_bytes: &[u8]) -> AnyHash {
        let hash_bytes: Vec<u8> = match self {
            HashAlgorithm::Sha256 => {
                let mut d = Sha256::new();
                d.update(content_bytes);
                d.finalize().deref().into()
            }
        };

        AnyHash {
            algo: *self,
            bytes: hash_bytes,
        }
    }
}

#[derive(Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct AnyHash {
    pub(crate) algo: HashAlgorithm,
    pub(crate) bytes: Vec<u8>,
}

impl AnyHash {
    pub fn new(algo: HashAlgorithm, bytes: Vec<u8>) -> AnyHash {
        AnyHash { algo, bytes }
    }

    pub fn algorithm(&self) -> HashAlgorithm {
        self.algo
    }

    pub fn bytes(&self) -> &[u8] {
        &self.bytes
    }
}

impl fmt::Display for AnyHash {
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        write!(f, "{}:{}", self.algo, hex::encode(self.bytes.as_slice()))
    }
}

impl fmt::Debug for AnyHash {
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        write!(f, "{}:{}", self.algo, hex::encode(self.bytes.as_slice()))
    }
}

impl FromStr for AnyHash {
    type Err = AnyHashError;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let (algo_part, bytes_part) = s
            .split_once(':')
            .ok_or_else(|| AnyHashError::IncorrectStructure(s.matches(':').count() + 1))?;

        if bytes_part.chars().any(|c| "ABCDEF".contains(c)) {
            return Err(AnyHashError::UppercaseHex);
        }

        let algo = algo_part.parse::<HashAlgorithm>()?;
        let bytes = hex::decode(bytes_part)?;

        Ok(AnyHash { algo, bytes })
    }
}

#[derive(Error, Debug)]
pub enum AnyHashError {
    #[error("expected two parts for hash; found {0}")]
    IncorrectStructure(usize),

    #[error("unable to parse hash algorithm: {0}")]
    InvalidHashAlgorithm(#[from] Error),

    #[error("hash contained uppercase hex values")]
    UppercaseHex,

    #[error("hexadecimal decode failed: {0}")]
    InvalidHex(#[from] hex::FromHexError),
}

impl Serialize for AnyHash {
    fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
        serializer.serialize_str(&self.to_string())
    }
}

impl<'de> Deserialize<'de> for AnyHash {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: serde::Deserializer<'de>,
    {
        Self::from_str(&String::deserialize(deserializer)?).map_err(serde::de::Error::custom)
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_sha256_labeled_digest() {
        let input = b"The quick brown fox jumped over the lazy dog";
        let output = HashAlgorithm::Sha256.digest(input);
        let output = format!("{}", output);

        let expected = "sha256:7d38b5cd25a2baf85ad3bb5b9311383e671a8a142eb302b324d4a5fba8748c69";

        assert_eq!(output, expected)
    }

    #[test]
    fn test_labeled_digest_parse_rejects_uppercase() {
        let digest_str = "sha256:7d38b5cd25a2baf85ad3bb5b9311383e671a8a142eb302b324d4a5fba8748c69";
        assert!(digest_str.parse::<AnyHash>().is_ok());

        let (algo, encoded) = digest_str.split_once(':').unwrap();
        let digest_str = String::from(algo) + ":" + &encoded.to_uppercase();
        assert!(digest_str.parse::<AnyHash>().is_err());
    }

    #[test]
    fn test_labeled_digest_roundtrip() {
        let input = "sha256:7d38b5cd25a2baf85ad3bb5b9311383e671a8a142eb302b324d4a5fba8748c69";
        let output = format!("{}", input.parse::<AnyHash>().unwrap());
        assert_eq!(input, &output);
    }
}