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
use anyhow::Error;
use std::fmt;

#[derive(Copy, Clone)]
pub enum ProgressTarget {
    Stdout,
    Stderr,
    Hidden,
}

impl From<ProgressTarget> for indicatif::ProgressDrawTarget {
    fn from(pt: ProgressTarget) -> Self {
        match pt {
            ProgressTarget::Stdout => Self::stdout(),
            ProgressTarget::Stderr => Self::stderr(),
            ProgressTarget::Hidden => Self::hidden(),
        }
    }
}

#[derive(Clone, PartialEq, Eq)]
pub struct Sha256(pub [u8; 32]);

impl fmt::Debug for Sha256 {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}", self)
    }
}

impl fmt::Display for Sha256 {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        for x in self.0 {
            write!(f, "{:02x}", x)?;
        }

        Ok(())
    }
}

impl<'slice> PartialEq<&'slice [u8]> for Sha256 {
    fn eq(&self, o: &&'slice [u8]) -> bool {
        self.0 == *o
    }
}

impl std::str::FromStr for Sha256 {
    type Err = Error;

    fn from_str(hex_str: &str) -> Result<Self, Self::Err> {
        anyhow::ensure!(
            hex_str.len() == 64,
            "sha256 string length is {} instead of 64",
            hex_str.len()
        );

        let mut digest = [0u8; 32];

        for (ind, chars) in hex_str.as_bytes().chunks(2).enumerate() {
            let mut cur = match chars[0] {
                b'A'..=b'F' => chars[0] - b'A' + 10,
                b'a'..=b'f' => chars[0] - b'a' + 10,
                b'0'..=b'9' => chars[0] - b'0',
                c => anyhow::bail!("invalid byte in hex string {}", c),
            };

            cur <<= 4;

            cur |= match chars[1] {
                b'A'..=b'F' => chars[1] - b'A' + 10,
                b'a'..=b'f' => chars[1] - b'a' + 10,
                b'0'..=b'9' => chars[1] - b'0',
                c => anyhow::bail!("invalid byte in hex checksum string {}", c),
            };

            digest[ind] = cur;
        }

        Ok(Self(digest))
    }
}

impl<'de> serde::Deserialize<'de> for Sha256 {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: serde::de::Deserializer<'de>,
    {
        struct Visitor;

        impl<'de> serde::de::Visitor<'de> for Visitor {
            type Value = Sha256;

            fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
                formatter.write_str("sha256 string")
            }

            fn visit_str<E>(self, value: &str) -> Result<Sha256, E>
            where
                E: serde::de::Error,
            {
                value.parse().map_err(serde::de::Error::custom)
            }
        }

        deserializer.deserialize_str(Visitor)
    }
}

pub(crate) fn serialize_sha256<S>(hash: &Sha256, serializer: S) -> Result<S::Ok, S::Error>
where
    S: serde::Serializer,
{
    serializer.serialize_str(&hash.to_string())
}

impl Sha256 {
    pub fn digest(buffer: &[u8]) -> Self {
        use sha2::Digest;

        let mut hasher = sha2::Sha256::new();
        hasher.update(buffer);
        let digest = hasher.finalize();

        Self(digest.into())
    }
}

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

    #[test]
    fn sha256() {
        let buffer = [3u8; 11];
        let digest = Sha256::digest(&buffer);

        let hex = digest.to_string();

        assert_eq!(digest, hex.parse::<Sha256>().unwrap());
    }
}