1use blake2::digest::consts::U32;
2use sha2::Digest;
3use std::pin::Pin;
4use std::task::{Context, Poll};
5use tokio::io::{AsyncReadExt, ReadBuf};
6
7use uv_pypi_types::{HashAlgorithm, HashDigest};
8
9#[derive(Debug)]
10pub enum Hasher {
11 Md5(md5::Md5),
12 Sha256(sha2::Sha256),
13 Sha384(sha2::Sha384),
14 Sha512(sha2::Sha512),
15 Blake2b(blake2::Blake2b<U32>),
16}
17
18impl Hasher {
19 pub fn update(&mut self, data: &[u8]) {
20 match self {
21 Self::Md5(hasher) => hasher.update(data),
22 Self::Sha256(hasher) => hasher.update(data),
23 Self::Sha384(hasher) => hasher.update(data),
24 Self::Sha512(hasher) => hasher.update(data),
25 Self::Blake2b(hasher) => hasher.update(data),
26 }
27 }
28}
29
30impl From<HashAlgorithm> for Hasher {
31 fn from(algorithm: HashAlgorithm) -> Self {
32 match algorithm {
33 HashAlgorithm::Md5 => Self::Md5(md5::Md5::new()),
34 HashAlgorithm::Sha256 => Self::Sha256(sha2::Sha256::new()),
35 HashAlgorithm::Sha384 => Self::Sha384(sha2::Sha384::new()),
36 HashAlgorithm::Sha512 => Self::Sha512(sha2::Sha512::new()),
37 HashAlgorithm::Blake2b => Self::Blake2b(blake2::Blake2b::new()),
38 }
39 }
40}
41
42impl From<Hasher> for HashDigest {
43 fn from(hasher: Hasher) -> Self {
44 match hasher {
45 Hasher::Md5(hasher) => Self {
46 algorithm: HashAlgorithm::Md5,
47 digest: format!("{:x}", hasher.finalize()).into(),
48 },
49 Hasher::Sha256(hasher) => Self {
50 algorithm: HashAlgorithm::Sha256,
51 digest: format!("{:x}", hasher.finalize()).into(),
52 },
53 Hasher::Sha384(hasher) => Self {
54 algorithm: HashAlgorithm::Sha384,
55 digest: format!("{:x}", hasher.finalize()).into(),
56 },
57 Hasher::Sha512(hasher) => Self {
58 algorithm: HashAlgorithm::Sha512,
59 digest: format!("{:x}", hasher.finalize()).into(),
60 },
61 Hasher::Blake2b(hasher) => Self {
62 algorithm: HashAlgorithm::Blake2b,
63 digest: format!("{:x}", hasher.finalize()).into(),
64 },
65 }
66 }
67}
68
69pub struct HashReader<'a, R> {
70 reader: R,
71 hashers: &'a mut [Hasher],
72}
73
74impl<'a, R> HashReader<'a, R>
75where
76 R: tokio::io::AsyncRead + Unpin,
77{
78 pub fn new(reader: R, hashers: &'a mut [Hasher]) -> Self {
79 HashReader { reader, hashers }
80 }
81
82 pub async fn finish(&mut self) -> Result<(), std::io::Error> {
84 while self.read(&mut vec![0; 8192]).await? > 0 {}
85
86 Ok(())
87 }
88}
89
90impl<R> tokio::io::AsyncRead for HashReader<'_, R>
91where
92 R: tokio::io::AsyncRead + Unpin,
93{
94 fn poll_read(
95 mut self: Pin<&mut Self>,
96 cx: &mut Context<'_>,
97 buf: &mut ReadBuf<'_>,
98 ) -> Poll<std::io::Result<()>> {
99 let reader = Pin::new(&mut self.reader);
100 match reader.poll_read(cx, buf) {
101 Poll::Ready(Ok(())) => {
102 for hasher in self.hashers.iter_mut() {
103 hasher.update(buf.filled());
104 }
105 Poll::Ready(Ok(()))
106 }
107 other => other,
108 }
109 }
110}