nix_compat/nixhash/
digester.rs1use std::io::Write;
2
3use sha2::Digest;
4
5use crate::nixhash::{HashAlgo, Sha256Digester};
6
7use super::NixHash;
8
9enum Inner {
10 Md5(md5::Md5),
11 Sha1(sha1::Sha1),
12 Sha256(Sha256Digester),
13 Sha512(sha2::Sha512),
14}
15
16pub struct NixHashDigester(Inner);
33
34impl NixHashDigester {
35 pub fn new(algo: HashAlgo) -> Self {
37 match algo {
38 HashAlgo::Md5 => Self(Inner::Md5(md5::Md5::new())),
39 HashAlgo::Sha1 => Self(Inner::Sha1(sha1::Sha1::new())),
40 HashAlgo::Sha256 => Self(Inner::Sha256(Sha256Digester::new())),
41 HashAlgo::Sha512 => Self(Inner::Sha512(sha2::Sha512::new())),
42 }
43 }
44
45 pub fn algorithm(&self) -> HashAlgo {
47 match self.0 {
48 Inner::Md5(_) => HashAlgo::Md5,
49 Inner::Sha1(_) => HashAlgo::Sha1,
50 Inner::Sha256(_) => HashAlgo::Sha256,
51 Inner::Sha512(_) => HashAlgo::Sha512,
52 }
53 }
54
55 pub fn update<C: AsRef<[u8]>>(&mut self, data: C) {
57 match &mut self.0 {
58 Inner::Md5(d) => d.update(data),
59 Inner::Sha1(d) => d.update(data),
60 Inner::Sha256(d) => d.update(data),
61 Inner::Sha512(d) => d.update(data),
62 }
63 }
64
65 pub fn finalize(self) -> NixHash {
67 match self.0 {
68 Inner::Md5(d) => {
69 let digest: [u8; HashAlgo::Md5.digest_length()] = d.finalize().into();
70 NixHash::Md5(digest)
71 }
72 Inner::Sha1(d) => {
73 let digest: [u8; HashAlgo::Sha1.digest_length()] = d.finalize().into();
74 NixHash::Sha1(digest)
75 }
76 Inner::Sha256(d) => d.finalize().into(),
77 Inner::Sha512(d) => {
78 let digest: [u8; HashAlgo::Sha512.digest_length()] = d.finalize().into();
79 NixHash::Sha512(Box::new(digest))
80 }
81 }
82 }
83}
84
85impl Write for NixHashDigester {
86 fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
87 self.update(buf);
88 Ok(buf.len())
89 }
90
91 fn flush(&mut self) -> std::io::Result<()> {
92 Ok(())
93 }
94}
95
96impl HashAlgo {
97 pub fn digest_bytes<C: AsRef<[u8]>>(&self, content: C) -> NixHash {
107 let mut c = NixHashDigester::new(*self);
108 c.update(content);
109 c.finalize()
110 }
111
112 pub fn digest_display<D: std::fmt::Display>(&self, value: D) -> NixHash {
124 let mut c = NixHashDigester::new(*self);
125 write!(c, "{value}").unwrap();
126 c.finalize()
127 }
128}
129
130#[cfg(test)]
131mod tests {
132 use std::sync::LazyLock;
133
134 use super::*;
135 use hex_literal::hex;
136
137 struct HashFormats {
138 input: &'static str,
139 algo: HashAlgo,
140 hash: NixHash,
141 }
142
143 const MD5_EMPTY: HashFormats = HashFormats {
145 input: "",
146 algo: HashAlgo::Md5,
147 hash: NixHash::Md5(hex!("d41d8cd98f00b204e9800998ecf8427e")),
148 };
149
150 const MD5_ABC: HashFormats = HashFormats {
152 input: "abc",
153 algo: HashAlgo::Md5,
154 hash: NixHash::Md5(hex!("900150983cd24fb0d6963f7d28e17f72")),
155 };
156
157 const SHA1_ABC: HashFormats = HashFormats {
159 input: "abc",
160 algo: HashAlgo::Sha1,
161 hash: NixHash::Sha1(hex!("a9993e364706816aba3e25717850c26c9cd0d89d")),
162 };
163
164 const SHA1_LONG: HashFormats = HashFormats {
166 input: "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq",
167 algo: HashAlgo::Sha1,
168 hash: NixHash::Sha1(hex!("84983e441c3bd26ebaae4aa1f95129e5e54670f1")),
169 };
170
171 const SHA256_ABC: HashFormats = HashFormats {
173 input: "abc",
174 algo: HashAlgo::Sha256,
175 hash: NixHash::Sha256(hex!(
176 "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"
177 )),
178 };
179
180 const SHA256_LONG: HashFormats = HashFormats {
182 input: "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq",
183 algo: HashAlgo::Sha256,
184 hash: NixHash::Sha256(hex!(
185 "248d6a61d20638b8e5c026930c3e6039a33ce45964ff2167f6ecedd419db06c1"
186 )),
187 };
188
189 static SHA512_ABC: LazyLock<HashFormats> = LazyLock::new(|| HashFormats {
191 input: "abc",
192 algo: HashAlgo::Sha512,
193 hash: NixHash::Sha512(Box::new(hex!(
194 "ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f"
195 ))),
196 });
197
198 static SHA512_LONG: LazyLock<HashFormats> = LazyLock::new(|| HashFormats {
200 input: "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu",
201 algo: HashAlgo::Sha512,
202 hash: NixHash::Sha512(Box::new(hex!(
203 "8e959b75dae313da8cf4f72814fc143f8f7779c6eb9f7fa17299aeadb6889018501d289e4900f7e4331b99dec4b5433ac7d329eeb6dd26545e96e55b874be909"
204 ))),
205 });
206
207 #[rstest_reuse::template]
208 #[rstest::rstest]
209 #[case::md5_empty(&MD5_EMPTY)]
210 #[case::md5_abc(&MD5_ABC)]
211 #[case::sha1_abc(&SHA1_ABC)]
212 #[case::sha1_long(&SHA1_LONG)]
213 #[case::sha256_abc(&SHA256_ABC)]
214 #[case::sha256_long(&SHA256_LONG)]
215 #[case::sha512_abc(&*SHA512_ABC)]
216 #[case::sha512_long(&*SHA512_LONG)]
217 fn hash_formats(#[case] hash: &HashFormats) {}
218
219 #[rstest_reuse::apply(hash_formats)]
221 fn digest_bytes(#[case] hash: &HashFormats) {
222 let actual = hash.algo.digest_bytes(hash.input);
223 assert_eq!(hash.hash, actual);
224 }
225
226 #[rstest_reuse::apply(hash_formats)]
228 fn digest_display(#[case] hash: &HashFormats) {
229 let actual = hash.algo.digest_display(format_args!("{}", hash.input));
230 assert_eq!(hash.hash, actual);
231 }
232
233 #[rstest_reuse::apply(hash_formats)]
235 fn digester(#[case] hash: &HashFormats) {
236 let mut ctx = NixHashDigester::new(hash.algo);
237 ctx.update(hash.input);
238 let actual = ctx.finalize();
239 assert_eq!(hash.hash, actual);
240 }
241
242 #[rstest_reuse::apply(hash_formats)]
244 fn digester_write(#[case] hash: &HashFormats) {
245 let mut ctx = NixHashDigester::new(hash.algo);
246 ctx.write_all(hash.input.as_bytes()).unwrap();
247 let actual = ctx.finalize();
248 assert_eq!(hash.hash, actual);
249 }
250}