use base64::alphabet::URL_SAFE; use base64::engine::general_purpose::PAD; use base64::engine::GeneralPurpose; use base64::Engine; use sha2::{Digest, Sha256}; use std::pin::Pin; use std::task::{Context, Poll}; use tokio::io::{self, AsyncRead, AsyncReadExt, ReadBuf}; pub const URL_SAFE_ENGINE: GeneralPurpose = GeneralPurpose::new(&URL_SAFE, PAD); pub struct BlobStreamReader { reader: R, hasher: Sha256, } impl AsyncRead for BlobStreamReader where R: AsyncRead + Unpin, { fn poll_read( mut self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &mut ReadBuf<'_>, ) -> Poll> { let before = buf.filled().len(); let poll = Pin::new(&mut self.reader).poll_read(cx, buf); let after = buf.filled().len(); if after > before { self.hasher.update(&buf.filled()[before..after]); } poll } } impl BlobStreamReader where R: AsyncRead + Unpin, { pub fn new(reader: R) -> Self { Self { reader, hasher: Sha256::new(), } } pub async fn finish(mut self) -> io::Result<(Vec, String)> { let mut buffer = Vec::new(); let _ = self.read_to_end(&mut buffer).await?; let hash = URL_SAFE_ENGINE.encode(self.hasher.finalize()); Ok((buffer, hash)) } } impl AsRef for BlobStreamReader where R: AsyncRead + Unpin, { fn as_ref(&self) -> &R { &self.reader } }