1#[cfg(feature = "native_openssl")]
41use crate::openssl_sha256::OpenSslSha256;
42
43#[cfg(feature = "async")]
44pub use async_digest::*;
45use sha2::digest::Output;
46use sha2::{Digest, Sha256};
47use std::fmt::Debug;
48use std::fs;
49use std::io;
50use std::io::{BufReader, Read};
51use std::path::Path;
52
53pub fn digest<D: Sha256Digest>(input: D) -> String {
65 input.digest()
66}
67
68pub fn try_digest<D: TrySha256Digest>(input: D) -> Result<String, D::Error> {
80 input.digest()
81}
82
83#[deprecated(since = "1.1.0", note = "Use new function `digest()` instead")]
95pub fn digest_bytes(input: &[u8]) -> String {
96 __digest__(input)
97}
98
99#[deprecated(since = "1.1.0", note = "Use new function `try_digest()` instead")]
111pub fn digest_file<P: AsRef<Path>>(path: P) -> Result<String, io::Error> {
112 let bytes = fs::read(path)?;
113 Ok(__digest__(&bytes))
114}
115
116pub trait Sha256Digest {
117 fn digest(self) -> String;
118}
119
120#[async_trait::async_trait]
121pub trait TrySha256Digest {
122 type Error: Debug;
123
124 fn digest(self) -> Result<String, Self::Error>;
125
126 #[cfg(feature = "async")]
127 async fn async_digest(self) -> Result<String, Self::Error>;
128
129 #[cfg(feature = "native_openssl")]
130 async fn async_openssl_digest(self) -> Result<String, Self::Error>;
131}
132
133impl<const N: usize> Sha256Digest for &[u8; N] {
134 fn digest(self) -> String {
135 __digest__(self)
136 }
137}
138
139impl Sha256Digest for &[u8] {
140 fn digest(self) -> String {
141 __digest__(self)
142 }
143}
144
145impl Sha256Digest for &Vec<u8> {
146 fn digest(self) -> String {
147 __digest__(self)
148 }
149}
150
151impl Sha256Digest for Vec<u8> {
152 fn digest(self) -> String {
153 __digest__(&self)
154 }
155}
156
157impl Sha256Digest for String {
158 fn digest(self) -> String {
159 __digest__(self.as_bytes())
160 }
161}
162
163impl Sha256Digest for &str {
164 fn digest(self) -> String {
165 __digest__(self.as_bytes())
166 }
167}
168
169impl Sha256Digest for char {
170 fn digest(self) -> String {
171 __digest__(self.encode_utf8(&mut [0; 4]).as_bytes())
172 }
173}
174
175impl Sha256Digest for &mut &str {
176 fn digest(self) -> String {
177 __digest__(self.as_bytes())
178 }
179}
180
181impl Sha256Digest for &String {
182 fn digest(self) -> String {
183 __digest__(self.as_bytes())
184 }
185}
186
187#[async_trait::async_trait]
188impl<P> TrySha256Digest for P
189where
190 P: AsRef<Path> + Send,
191{
192 type Error = io::Error;
193
194 fn digest(self) -> Result<String, Self::Error> {
195 let f = fs::File::open(self)?;
196 let reader = BufReader::new(f);
197 let sha = Sha256::new();
198 calc(reader, sha)
199 }
200
201 #[cfg(feature = "async")]
202 async fn async_digest(self) -> Result<String, Self::Error> {
203 let f = tokio::fs::File::open(self).await?;
204 let reader = tokio::io::BufReader::new(f);
205 let sha = Sha256::new();
206 async_calc(reader, sha).await
207 }
208
209 #[cfg(all(feature = "async", feature = "native_openssl"))]
210 async fn async_openssl_digest(self) -> Result<String, Self::Error> {
211 let f = tokio::fs::File::open(self).await?;
212 let reader = tokio::io::BufReader::new(f);
213 let sha = OpenSslSha256::new();
214 async_calc(reader, sha).await
215 }
216}
217
218fn __digest__(data: &[u8]) -> String {
219 hex::encode(Sha256::digest(data))
220}
221
222trait CalculatorInput {
223 fn read_inner(&mut self, buf: &mut [u8]) -> std::io::Result<usize>;
224}
225
226impl<T> CalculatorInput for T
227where
228 T: Read,
229{
230 fn read_inner(&mut self, buf: &mut [u8]) -> io::Result<usize> {
231 self.read(buf)
232 }
233}
234
235pub trait CalculatorSelector {
236 type FinishType: AsRef<[u8]>;
237 fn update_inner(&mut self, data: &[u8]);
238 fn finish_inner(self) -> Self::FinishType;
239}
240
241impl CalculatorSelector for Sha256 {
242 type FinishType = Output<Sha256>;
243
244 fn update_inner(&mut self, data: &[u8]) {
245 self.update(data)
246 }
247
248 fn finish_inner(self) -> Self::FinishType {
249 self.finalize()
250 }
251}
252
253fn calc<I, S>(mut input: I, mut selector: S) -> io::Result<String>
254where
255 I: CalculatorInput,
256 S: CalculatorSelector,
257{
258 let mut buf = [0u8; 1024];
259 loop {
260 let len = input.read_inner(&mut buf)?;
261 if len == 0 {
262 break;
263 }
264 selector.update_inner(&buf[0..len]);
265 }
266 let hash = selector.finish_inner();
267 Ok(hex::encode(hash))
268}
269
270#[cfg(feature = "async")]
271pub mod async_digest {
272 use crate::{CalculatorSelector, TrySha256Digest};
273 use bytes::BytesMut;
274 use std::io;
275
276 pub async fn try_async_digest<D: TrySha256Digest>(input: D) -> Result<String, D::Error> {
291 input.async_digest().await
292 }
293
294 #[cfg(feature = "native_openssl")]
308 pub async fn try_async_openssl_digest<D: TrySha256Digest>(
309 input: D,
310 ) -> Result<String, D::Error> {
311 input.async_openssl_digest().await
312 }
313
314 #[async_trait::async_trait]
315 pub trait AsyncCalculatorInput {
316 async fn read_inner(&mut self, buf: &mut BytesMut) -> io::Result<usize>;
317 }
318
319 pub async fn async_calc<I, S>(mut input: I, mut selector: S) -> io::Result<String>
320 where
321 I: AsyncCalculatorInput,
322 S: CalculatorSelector,
323 {
324 let mut buf = BytesMut::with_capacity(1024);
325 loop {
326 buf.clear();
327 let len = input.read_inner(&mut buf).await?;
328 if len == 0 {
329 break;
330 }
331 selector.update_inner(&buf[0..len]);
332 }
333 let hash = selector.finish_inner();
334 Ok(hex::encode(hash))
335 }
336
337 #[async_trait::async_trait]
338 impl<R> AsyncCalculatorInput for tokio::io::BufReader<R>
339 where
340 R: tokio::io::AsyncRead + Unpin + Send,
341 {
342 async fn read_inner(&mut self, buf: &mut BytesMut) -> io::Result<usize> {
343 use tokio::io::AsyncReadExt;
344
345 self.read_buf(buf).await
346 }
347 }
348}
349
350#[cfg(feature = "native_openssl")]
351mod openssl_sha256 {
352 use crate::CalculatorSelector;
353
354 pub type OpenSslSha256 = openssl::sha::Sha256;
355
356 impl CalculatorSelector for OpenSslSha256 {
357 type FinishType = [u8; 32];
358
359 fn update_inner(&mut self, data: &[u8]) {
360 self.update(data)
361 }
362
363 fn finish_inner(self) -> Self::FinishType {
364 self.finish()
365 }
366 }
367}
368
369#[cfg(test)]
370mod tests {
371 use super::*;
372
373 #[cfg(feature = "native_openssl")]
374 #[test]
375 fn test_openssl() {
376 let input = Path::new("./foo.file");
377 let f = fs::File::open(input).unwrap();
378 let reader = BufReader::new(f);
379 let sha = openssl::sha::Sha256::new();
380 assert_eq!(
381 "433855b7d2b96c23a6f60e70c655eb4305e8806b682a9596a200642f947259b1",
382 calc(reader, sha).unwrap()
383 );
384 }
385
386 #[test]
387 fn test_sha256() {
388 let input = Path::new("./foo.file");
389 let f = fs::File::open(input).unwrap();
390 let reader = BufReader::new(f);
391 let sha = Sha256::new();
392 assert_eq!(
393 "433855b7d2b96c23a6f60e70c655eb4305e8806b682a9596a200642f947259b1",
394 calc(reader, sha).unwrap()
395 );
396 }
397
398 #[cfg(all(feature = "async", feature = "native_openssl"))]
399 #[tokio::test]
400 async fn test_async_openssl() {
401 let input = Path::new("./foo.file");
402 let f = tokio::fs::File::open(input).await.unwrap();
403 let reader = tokio::io::BufReader::new(f);
404 let sha = openssl::sha::Sha256::new();
405 assert_eq!(
406 "433855b7d2b96c23a6f60e70c655eb4305e8806b682a9596a200642f947259b1",
407 async_calc(reader, sha).await.unwrap()
408 );
409 }
410
411 #[cfg(feature = "async")]
412 #[tokio::test]
413 async fn test_async() {
414 let input = Path::new("./foo.file");
415 let f = tokio::fs::File::open(input).await.unwrap();
416 let reader = tokio::io::BufReader::new(f);
417 let sha = Sha256::new();
418 assert_eq!(
419 "433855b7d2b96c23a6f60e70c655eb4305e8806b682a9596a200642f947259b1",
420 async_calc(reader, sha).await.unwrap()
421 );
422 }
423
424 #[cfg(all(feature = "async", feature = "native_openssl"))]
425 #[tokio::test]
426 async fn test_try_async_openssl_digest() {
427 let hash = try_async_openssl_digest("./foo.file").await.unwrap();
428 assert_eq!(
429 "433855b7d2b96c23a6f60e70c655eb4305e8806b682a9596a200642f947259b1",
430 hash
431 );
432 }
433
434 #[cfg(feature = "async")]
435 #[tokio::test]
436 async fn test_try_async_digest() {
437 let hash = try_async_digest("./foo.file").await.unwrap();
438 assert_eq!(
439 "433855b7d2b96c23a6f60e70c655eb4305e8806b682a9596a200642f947259b1",
440 hash
441 );
442 }
443
444 #[cfg(feature = "async")]
445 #[tokio::test]
446 async fn test_async_parity() {
447 let bytes = (0..0x1000).map(|v| (v % 256) as u8).collect::<Vec<_>>();
448 let val = digest(&bytes);
449
450 let async_res = {
451 let bytes = &bytes;
452 let (client, mut server) = tokio::io::duplex(64);
455 let reader = tokio::io::BufReader::new(client);
456 let sha = Sha256::new();
457
458 use tokio::io::AsyncWriteExt;
459 tokio::join! {
460 async_calc(reader, sha),
461 async move {
462 server.write_all(&bytes[..]).await.unwrap();
463 core::mem::drop(server);
464 }
465 }
466 .0
467 .unwrap()
468 };
469
470 let sync_res = {
471 let reader = BufReader::new(&*bytes);
472 let sha = Sha256::new();
473 calc(reader, sha).unwrap()
474 };
475 assert_eq!(val, async_res);
476 assert_eq!(async_res, sync_res);
477 }
478
479 #[cfg(all(feature = "async", feature = "native_openssl"))]
480 #[tokio::test]
481 async fn test_async_parity_openssl() {
482 let bytes = (0..0x1000).map(|v| (v % 256) as u8).collect::<Vec<_>>();
483 let val = digest(&bytes);
484
485 let async_res = {
486 let bytes = &bytes;
487 let (client, mut server) = tokio::io::duplex(64);
490 let reader = tokio::io::BufReader::new(client);
491 let sha = OpenSslSha256::new();
492
493 use tokio::io::AsyncWriteExt;
494 tokio::join! {
495 async_calc(reader, sha),
496 async move {
497 server.write_all(&bytes[..]).await.unwrap();
498 core::mem::drop(server);
499 }
500 }
501 .0
502 .unwrap()
503 };
504
505 let sync_res = {
506 let reader = BufReader::new(&*bytes);
507 let sha = OpenSslSha256::new();
508 calc(reader, sha).unwrap()
509 };
510 assert_eq!(val, async_res);
511 assert_eq!(async_res, sync_res);
512 }
513}