cargo_lock/package/
checksum.rs1use crate::{Error, Result};
4use serde::{Deserialize, Serialize, de, ser};
5use std::{fmt, str::FromStr};
6
7#[derive(Clone, Eq, Hash, PartialEq, PartialOrd, Ord)]
9pub enum Checksum {
10 Sha256([u8; 32]),
12}
13
14impl Checksum {
15 pub fn is_sha256(&self) -> bool {
17 self.as_sha256().is_some()
18 }
19
20 pub fn as_sha256(&self) -> Option<[u8; 32]> {
22 match self {
23 Checksum::Sha256(digest) => Some(*digest),
24 }
25 }
26}
27
28impl From<[u8; 32]> for Checksum {
29 fn from(bytes: [u8; 32]) -> Checksum {
30 Checksum::Sha256(bytes)
31 }
32}
33
34impl FromStr for Checksum {
35 type Err = Error;
36
37 fn from_str(s: &str) -> Result<Self> {
38 if s.len() != 64 {
39 return Err(Error::Parse(format!(
40 "invalid checksum: expected 64 hex chars, got {}",
41 s.len()
42 )));
43 }
44
45 let mut digest = [0u8; 32];
46
47 for (i, byte) in digest.iter_mut().enumerate() {
48 *byte = u8::from_str_radix(&s[(i * 2)..=(i * 2) + 1], 16)?;
49 }
50
51 Ok(Checksum::Sha256(digest))
52 }
53}
54
55impl fmt::Debug for Checksum {
56 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
57 match self {
58 Checksum::Sha256(_) => write!(f, "Sha256({self:x})"),
59 }
60 }
61}
62
63impl fmt::Display for Checksum {
64 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
65 write!(f, "{self:x}")
66 }
67}
68
69impl fmt::LowerHex for Checksum {
70 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
71 match self {
72 Checksum::Sha256(digest) => {
73 for b in digest {
74 write!(f, "{b:02x}")?;
75 }
76 }
77 }
78
79 Ok(())
80 }
81}
82
83impl fmt::UpperHex for Checksum {
84 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
85 match self {
86 Checksum::Sha256(digest) => {
87 for b in digest {
88 write!(f, "{b:02X}")?;
89 }
90 }
91 }
92
93 Ok(())
94 }
95}
96
97impl<'de> Deserialize<'de> for Checksum {
98 fn deserialize<D: de::Deserializer<'de>>(
99 deserializer: D,
100 ) -> std::result::Result<Self, D::Error> {
101 let hex = String::deserialize(deserializer)?;
102 hex.parse().map_err(de::Error::custom)
103 }
104}
105
106impl Serialize for Checksum {
107 fn serialize<S: ser::Serializer>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error> {
108 self.to_string().serialize(serializer)
109 }
110}
111
112#[cfg(test)]
113mod tests {
114 use super::{Checksum, Error};
115
116 #[test]
117 fn checksum_round_trip() {
118 let checksum_str = "af6f3550d8dff9ef7dc34d384ac6f107e5d31c8f57d9f28e0081503f547ac8f5";
119 let checksum = checksum_str.parse::<Checksum>().unwrap();
120 assert_eq!(checksum_str, checksum.to_string());
121 }
122
123 #[test]
124 fn invalid_checksum() {
125 let invalid_str = "af6f3550d8dff9ef7dc34d384ac6f107e5d31c8f57d9f28e0081503f547ac8f";
127 let error = invalid_str.parse::<Checksum>().err().unwrap();
128 assert!(matches!(error, Error::Parse(_)));
129 }
130}