1#![cfg_attr(docsrs, feature(doc_auto_cfg))]
63#![forbid(unsafe_code)]
64
65use std::io::{self, BufRead, Read};
66#[cfg(feature = "async-runtime-tokio")]
67use std::pin::{pin, Pin};
68#[cfg(feature = "async-runtime-tokio")]
69use std::task::{Context, Poll};
70
71use chksum_core::Hash;
72#[cfg(feature = "async-runtime-tokio")]
73use tokio::io::{AsyncRead, AsyncReadExt, ReadBuf};
74
75pub fn new<R, H>(inner: R) -> Reader<R, H>
77where
78 R: Read,
79 H: Hash,
80{
81 Reader::new(inner)
82}
83
84pub fn with_hash<R, H>(inner: R, hash: H) -> Reader<R, H>
86where
87 R: Read,
88 H: Hash,
89{
90 Reader::with_hash(inner, hash)
91}
92
93#[cfg(feature = "async-runtime-tokio")]
94pub fn async_new<R, H>(inner: R) -> AsyncReader<R, H>
96where
97 R: AsyncReadExt,
98 H: Hash,
99{
100 AsyncReader::new(inner)
101}
102
103#[cfg(feature = "async-runtime-tokio")]
104pub fn async_with_hash<R, H>(inner: R, hash: H) -> AsyncReader<R, H>
106where
107 R: AsyncReadExt,
108 H: Hash,
109{
110 AsyncReader::with_hash(inner, hash)
111}
112
113#[derive(Clone, Debug, PartialEq, Eq)]
115pub struct Reader<R, H>
116where
117 R: Read,
118 H: Hash,
119{
120 inner: R,
121 hash: H,
122}
123
124impl<R, H> Reader<R, H>
125where
126 R: Read,
127 H: Hash,
128{
129 pub fn new(inner: R) -> Self {
131 let hash = H::default();
132 Self::with_hash(inner, hash)
133 }
134
135 #[must_use]
137 pub const fn with_hash(inner: R, hash: H) -> Self {
138 Self { inner, hash }
139 }
140
141 #[must_use]
143 pub fn into_inner(self) -> R {
144 let Self { inner, .. } = self;
145 inner
146 }
147
148 #[must_use]
150 pub fn digest(&self) -> H::Digest {
151 self.hash.digest()
152 }
153}
154
155impl<R, H> Read for Reader<R, H>
156where
157 R: Read,
158 H: Hash,
159{
160 fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
161 let n = self.inner.read(buf)?;
162 self.hash.update(&buf[..n]);
163 Ok(n)
164 }
165}
166
167impl<R, H> BufRead for Reader<R, H>
168where
169 R: BufRead,
170 H: Hash,
171{
172 fn consume(&mut self, amt: usize) {
173 self.inner.consume(amt);
174 }
175
176 fn fill_buf(&mut self) -> io::Result<&[u8]> {
177 self.inner.fill_buf()
178 }
179}
180
181#[cfg(feature = "async-runtime-tokio")]
183#[derive(Clone, Debug, PartialEq, Eq)]
184pub struct AsyncReader<R, H>
185where
186 R: AsyncReadExt,
187 H: Hash,
188{
189 inner: R,
190 hash: H,
191}
192
193#[cfg(feature = "async-runtime-tokio")]
194impl<R, H> AsyncReader<R, H>
195where
196 R: AsyncReadExt,
197 H: Hash,
198{
199 pub fn new(inner: R) -> Self {
201 let hash = H::default();
202 Self::with_hash(inner, hash)
203 }
204
205 #[must_use]
207 pub const fn with_hash(inner: R, hash: H) -> Self {
208 Self { inner, hash }
209 }
210
211 #[must_use]
213 pub fn into_inner(self) -> R {
214 let Self { inner, .. } = self;
215 inner
216 }
217
218 #[must_use]
220 pub fn digest(&self) -> H::Digest {
221 self.hash.digest()
222 }
223}
224
225#[cfg(feature = "async-runtime-tokio")]
226impl<R, H> AsyncRead for AsyncReader<R, H>
227where
228 R: AsyncRead + Unpin,
229 H: Hash + Unpin,
230{
231 fn poll_read(self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &mut ReadBuf<'_>) -> Poll<io::Result<()>> {
232 let Self { inner, hash } = self.get_mut();
233 match pin!(inner).poll_read(cx, buf) {
234 Poll::Ready(Ok(())) => {
235 hash.update(buf.filled());
236 Poll::Ready(Ok(()))
237 },
238 poll => poll,
239 }
240 }
241}