nix_compat/nixhash/
sha256.rs1use std::fmt;
2use std::io::Write;
3
4use data_encoding::{HEXLOWER, HEXUPPER};
5use sha2::Digest;
6
7use crate::nixhash::HashAlgo;
8
9use super::NixHash;
10
11type Sha256Array = [u8; Sha256::digest_length()];
12
13#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
15pub struct Sha256(Sha256Array);
16impl Sha256 {
17 pub const fn digest_length() -> usize {
18 HashAlgo::Sha256.digest_length()
19 }
20
21 pub const fn new(digest: Sha256Array) -> Self {
22 Self(digest)
23 }
24
25 pub fn digest_bytes<C: AsRef<[u8]>>(content: C) -> Self {
35 let mut w = Sha256Digester::new();
36 w.update(content.as_ref());
37 w.finalize()
38 }
39
40 pub fn digest_display<D: fmt::Display>(value: D) -> Self {
52 let mut w = Sha256Digester::new();
53 write!(&mut w, "{value}").unwrap();
54 w.finalize()
55 }
56
57 pub const fn as_bytes(&self) -> &[u8] {
58 &self.0
59 }
60
61 pub const fn into_bytes(self) -> Sha256Array {
62 self.0
63 }
64}
65
66impl fmt::LowerHex for Sha256 {
67 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
68 write!(f, "{}", HEXLOWER.encode_display(self.as_bytes()))
69 }
70}
71
72impl fmt::UpperHex for Sha256 {
73 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
74 write!(f, "{}", HEXUPPER.encode_display(self.as_bytes()))
75 }
76}
77
78impl AsRef<[u8]> for Sha256 {
79 fn as_ref(&self) -> &[u8] {
80 self.as_bytes()
81 }
82}
83
84impl std::borrow::Borrow<[u8]> for Sha256 {
85 fn borrow(&self) -> &[u8] {
86 self.as_bytes()
87 }
88}
89
90impl std::ops::Deref for Sha256 {
91 type Target = [u8];
92
93 fn deref(&self) -> &Self::Target {
94 self.as_bytes()
95 }
96}
97
98impl From<Sha256Array> for Sha256 {
99 fn from(digest: Sha256Array) -> Self {
100 Sha256::new(digest)
101 }
102}
103
104impl From<Sha256> for Sha256Array {
105 fn from(value: Sha256) -> Self {
106 value.into_bytes()
107 }
108}
109
110impl From<Sha256> for NixHash {
111 fn from(value: Sha256) -> Self {
112 NixHash::Sha256(value.into_bytes())
113 }
114}
115
116impl PartialEq<Sha256Array> for Sha256 {
117 fn eq(&self, other: &Sha256Array) -> bool {
118 &self.0 == other
119 }
120}
121
122impl PartialEq<Sha256> for Sha256Array {
123 fn eq(&self, other: &Sha256) -> bool {
124 self == &other.0
125 }
126}
127
128impl PartialEq<[u8]> for Sha256 {
129 fn eq(&self, other: &[u8]) -> bool {
130 self.0 == other
131 }
132}
133
134impl PartialEq<Sha256> for [u8] {
135 fn eq(&self, other: &Sha256) -> bool {
136 self == other.0
137 }
138}
139
140impl PartialEq<NixHash> for Sha256 {
141 fn eq(&self, other: &NixHash) -> bool {
142 matches!(other, NixHash::Sha256(sha256) if self == sha256)
143 }
144}
145
146impl PartialEq<Sha256> for NixHash {
147 fn eq(&self, other: &Sha256) -> bool {
148 matches!(self, NixHash::Sha256(sha256) if other == sha256)
149 }
150}
151
152pub struct Sha256Digester(sha2::Sha256);
169impl Sha256Digester {
170 pub fn new() -> Self {
172 Self(sha2::Sha256::new())
173 }
174
175 pub fn update<C: AsRef<[u8]>>(&mut self, data: C) {
177 self.0.update(data);
178 }
179
180 pub fn finalize(self) -> Sha256 {
182 let digest: [u8; HashAlgo::Sha256.digest_length()] = self.0.finalize().into();
183 Sha256::new(digest)
184 }
185}
186
187impl Default for Sha256Digester {
188 fn default() -> Self {
189 Self::new()
190 }
191}
192
193impl Write for Sha256Digester {
194 fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
195 self.update(buf);
196 Ok(buf.len())
197 }
198
199 fn flush(&mut self) -> std::io::Result<()> {
200 Ok(())
201 }
202}
203
204#[macro_export]
213macro_rules! format_sha256 {
214 ($($args:tt)*) => {
215 ::nix_compat::nixhash::Sha256::digest_display(format_args!($($args)*))
216 };
217}
218
219#[cfg(test)]
220mod tests {
221 use super::*;
222 use hex_literal::hex;
223
224 struct Sha256Formats {
225 input: &'static str,
226 hash: Sha256,
227 base16: &'static str,
228 }
229
230 const SHA256_ABC: Sha256Formats = Sha256Formats {
232 input: "abc",
233 hash: Sha256::new(hex!(
234 "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"
235 )),
236 base16: "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad",
237 };
238
239 const SHA256_LONG: Sha256Formats = Sha256Formats {
241 input: "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq",
242 hash: Sha256::new(hex!(
243 "248d6a61d20638b8e5c026930c3e6039a33ce45964ff2167f6ecedd419db06c1"
244 )),
245 base16: "248d6a61d20638b8e5c026930c3e6039a33ce45964ff2167f6ecedd419db06c1",
246 };
247
248 #[rstest_reuse::template]
249 #[rstest::rstest]
250 #[case::abc(SHA256_ABC)]
251 #[case::long(SHA256_LONG)]
252 fn hash_formats(#[case] hash: Sha256Formats) {}
253
254 #[rstest_reuse::apply(hash_formats)]
256 fn lower_hex(#[case] hash: Sha256Formats) {
257 let actual = format!("{:x}", hash.hash);
258 assert_eq!(hash.base16, actual);
259 }
260
261 #[rstest_reuse::apply(hash_formats)]
263 fn upper_hex(#[case] hash: Sha256Formats) {
264 let expected = hash.base16.to_uppercase();
265 let actual = format!("{:X}", hash.hash);
266 assert_eq!(expected, actual);
267 }
268
269 #[rstest_reuse::apply(hash_formats)]
271 fn digest_bytes(#[case] hash: Sha256Formats) {
272 let actual = Sha256::digest_bytes(hash.input);
273 assert_eq!(hash.hash, actual);
274 }
275
276 #[rstest_reuse::apply(hash_formats)]
278 fn digest_display(#[case] hash: Sha256Formats) {
279 let actual = Sha256::digest_display(format_args!("{}", hash.input));
280 assert_eq!(hash.hash, actual);
281 }
282
283 #[rstest_reuse::apply(hash_formats)]
285 fn format_sha256_test(#[case] hash: Sha256Formats) {
286 let actual = format_sha256!("{}", hash.input);
287 assert_eq!(hash.hash, actual);
288 }
289
290 #[rstest_reuse::apply(hash_formats)]
292 fn digester(#[case] hash: Sha256Formats) {
293 let mut ctx = Sha256Digester::new();
294 ctx.update(hash.input);
295 let actual = ctx.finalize();
296 assert_eq!(hash.hash, actual);
297 }
298
299 #[rstest_reuse::apply(hash_formats)]
301 fn digester_write(#[case] hash: Sha256Formats) {
302 let mut ctx = Sha256Digester::new();
303 ctx.write_all(hash.input.as_bytes()).unwrap();
304 let actual = ctx.finalize();
305 assert_eq!(hash.hash, actual);
306 }
307}