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
use crate::{Error, ErrorKind};
use serde::{de, ser, Deserialize, Serialize};
pub use std::{convert::TryFrom, fmt, str::FromStr};
#[derive(Clone, Eq, Hash, PartialEq, PartialOrd, Ord)]
pub enum Checksum {
Sha256([u8; 32]),
}
impl Checksum {
pub fn is_sha256(&self) -> bool {
self.as_sha256().is_some()
}
pub fn as_sha256(&self) -> Option<[u8; 32]> {
match self {
Checksum::Sha256(digest) => Some(*digest),
}
}
}
impl From<[u8; 32]> for Checksum {
fn from(bytes: [u8; 32]) -> Checksum {
Checksum::Sha256(bytes)
}
}
impl FromStr for Checksum {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Error> {
if s.len() != 64 {
fail!(
ErrorKind::Parse,
"invalid checksum: expected 64 hex chars, got {}",
s.len()
);
}
let mut digest = [0u8; 32];
for (i, byte) in digest.iter_mut().enumerate() {
*byte = u8::from_str_radix(&s[(i * 2)..=(i * 2) + 1], 16)?;
}
Ok(Checksum::Sha256(digest))
}
}
impl fmt::Debug for Checksum {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Checksum::Sha256(_) => write!(f, "Sha256({:x})", self),
}
}
}
impl fmt::Display for Checksum {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:x}", self)
}
}
impl fmt::LowerHex for Checksum {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Checksum::Sha256(digest) => {
for b in digest {
write!(f, "{:02x}", b)?;
}
}
}
Ok(())
}
}
impl fmt::UpperHex for Checksum {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Checksum::Sha256(digest) => {
for b in digest {
write!(f, "{:02X}", b)?;
}
}
}
Ok(())
}
}
impl<'de> Deserialize<'de> for Checksum {
fn deserialize<D: de::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
use de::Error;
let hex = String::deserialize(deserializer)?;
hex.parse().map_err(D::Error::custom)
}
}
impl Serialize for Checksum {
fn serialize<S: ser::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
self.to_string().serialize(serializer)
}
}
#[cfg(test)]
mod tests {
use super::{Checksum, ErrorKind};
#[test]
fn checksum_round_trip() {
let checksum_str = "af6f3550d8dff9ef7dc34d384ac6f107e5d31c8f57d9f28e0081503f547ac8f5";
let checksum = checksum_str.parse::<Checksum>().unwrap();
assert_eq!(checksum_str, checksum.to_string());
}
#[test]
fn invalid_checksum() {
let invalid_str = "af6f3550d8dff9ef7dc34d384ac6f107e5d31c8f57d9f28e0081503f547ac8f";
let error = invalid_str.parse::<Checksum>().err().unwrap();
assert_eq!(error.kind(), ErrorKind::Parse);
}
}