1#![cfg_attr(docsrs, feature(doc_auto_cfg))]
43#![forbid(unsafe_code)]
44
45mod error;
46#[cfg(feature = "async-runtime-tokio")]
47mod tokio;
48
49use std::fmt::{Display, LowerHex, UpperHex};
50use std::fs::{read_dir, DirEntry, File, ReadDir};
51use std::io::{self, BufRead, BufReader, IsTerminal, Stdin, StdinLock};
52use std::path::{Path, PathBuf};
53
54#[cfg(feature = "async-runtime-tokio")]
55use async_trait::async_trait;
56#[doc(no_inline)]
57pub use chksum_hash_core as hash;
58
59pub use crate::error::{Error, Result};
60
61#[must_use]
63pub fn default<H>() -> H
64where
65 H: Hash,
66{
67 Default::default()
68}
69
70pub fn hash<T>(data: impl Hashable) -> T::Digest
72where
73 T: Hash,
74{
75 data.hash::<T>()
76}
77
78pub fn chksum<T>(mut data: impl Chksumable) -> Result<T::Digest>
80where
81 T: Hash,
82{
83 data.chksum::<T>()
84}
85
86#[cfg(feature = "async-runtime-tokio")]
88pub async fn async_chksum<T>(mut data: impl AsyncChksumable) -> Result<T::Digest>
89where
90 T: Hash + Send,
91{
92 data.chksum::<T>().await
93}
94
95pub trait Digest: Display {
97 #[must_use]
99 fn as_bytes(&self) -> &[u8]
100 where
101 Self: AsRef<[u8]>,
102 {
103 self.as_ref()
104 }
105
106 #[must_use]
108 fn to_hex_lowercase(&self) -> String
109 where
110 Self: LowerHex,
111 {
112 format!("{self:x}")
113 }
114
115 #[must_use]
117 fn to_hex_uppercase(&self) -> String
118 where
119 Self: UpperHex,
120 {
121 format!("{self:X}")
122 }
123}
124
125pub trait Hash: Default {
127 type Digest: Digest;
129
130 #[must_use]
132 fn hash<T>(data: T) -> Self::Digest
133 where
134 T: AsRef<[u8]>,
135 {
136 let mut hash = Self::default();
137 hash.update(data);
138 hash.digest()
139 }
140
141 fn update<T>(&mut self, data: T)
143 where
144 T: AsRef<[u8]>;
145
146 fn reset(&mut self);
148
149 #[must_use]
151 fn digest(&self) -> Self::Digest;
152}
153
154pub trait Hashable: AsRef<[u8]> {
156 fn hash<H>(&self) -> H::Digest
158 where
159 H: Hash,
160 {
161 let mut hash = H::default();
162 self.hash_with(&mut hash);
163 hash.digest()
164 }
165
166 fn hash_with<H>(&self, hash: &mut H)
168 where
169 H: Hash,
170 {
171 hash.update(self);
172 }
173}
174
175macro_rules! impl_hashable {
176 ([$t:ty; LENGTH], $($rest:tt)+) => {
177 impl_hashable!([$t; LENGTH]);
178 impl_hashable!($($rest)*);
179 };
180
181 ($t:ty, $($rest:tt)+) => {
182 impl_hashable!($t);
183 impl_hashable!($($rest)*);
184 };
185
186 ([$t:ty; LENGTH]) => {
187 impl<const LENGTH: usize> Hashable for [$t; LENGTH] {}
188 };
189
190 ($t:ty) => {
191 impl Hashable for $t {}
192 };
193}
194
195impl_hashable!(&[u8], [u8; LENGTH], Vec<u8>, &str, String);
196
197impl<T> Hashable for &T where T: Hashable {}
198
199impl<T> Hashable for &mut T where T: Hashable {}
200
201pub trait Chksumable {
203 fn chksum<H>(&mut self) -> Result<H::Digest>
205 where
206 H: Hash,
207 {
208 let mut hash = H::default();
209 self.chksum_with(&mut hash)?;
210 Ok(hash.digest())
211 }
212
213 fn chksum_with<H>(&mut self, hash: &mut H) -> Result<()>
215 where
216 H: Hash;
217}
218
219impl<T> Chksumable for T
220where
221 T: Hashable,
222{
223 fn chksum_with<H>(&mut self, hash: &mut H) -> Result<()>
224 where
225 H: Hash,
226 {
227 self.hash_with(hash);
228 Ok(())
229 }
230}
231
232macro_rules! impl_chksumable {
233 ($($t:ty),+ => $i:tt) => {
234 $(
235 impl Chksumable for $t $i
236 )*
237 };
238}
239
240impl_chksumable!(Path, &Path, &mut Path => {
241 fn chksum_with<H>(&mut self, hash: &mut H) -> Result<()>
242 where
243 H: Hash,
244 {
245 let metadata = self.metadata()?;
246 if metadata.is_dir() {
247 read_dir(self)?.chksum_with(hash)
248 } else {
249 File::open(self)?.chksum_with(hash)
251 }
252 }
253});
254
255impl_chksumable!(PathBuf, &PathBuf, &mut PathBuf => {
256 fn chksum_with<H>(&mut self, hash: &mut H) -> Result<()>
257 where
258 H: Hash,
259 {
260 Chksumable::chksum_with(&mut self.as_path(), hash)
261 }
262});
263
264impl_chksumable!(File, &File, &mut File => {
265 fn chksum_with<H>(&mut self, hash: &mut H) -> Result<()>
266 where
267 H: Hash,
268 {
269 if self.is_terminal() {
270 return Err(Error::IsTerminal);
271 }
272
273 let mut reader = BufReader::new(self);
274 loop {
275 let buffer = reader.fill_buf()?;
276 let length = buffer.len();
277 if length == 0 {
278 break;
279 }
280 buffer.hash_with(hash);
281 reader.consume(length);
282 }
283 Ok(())
284 }
285});
286
287impl_chksumable!(DirEntry, &DirEntry, &mut DirEntry => {
288 fn chksum_with<H>(&mut self, hash: &mut H) -> Result<()>
289 where
290 H: Hash,
291 {
292 Chksumable::chksum_with(&mut self.path(), hash)
293 }
294});
295
296impl_chksumable!(ReadDir, &mut ReadDir => {
297 fn chksum_with<H>(&mut self, hash: &mut H) -> Result<()>
298 where
299 H: Hash,
300 {
301 let dir_entries: io::Result<Vec<DirEntry>> = self.collect();
302 let mut dir_entries = dir_entries?;
303 dir_entries.sort_by_key(DirEntry::path);
304 dir_entries
305 .into_iter()
306 .try_for_each(|mut dir_entry| dir_entry.chksum_with(hash))?;
307 Ok(())
308 }
309});
310
311impl_chksumable!(Stdin, &Stdin, &mut Stdin => {
312 fn chksum_with<H>(&mut self, hash: &mut H) -> Result<()>
313 where
314 H: Hash,
315 {
316 self.lock().chksum_with(hash)
317 }
318});
319
320impl_chksumable!(StdinLock<'_>, &mut StdinLock<'_> => {
321 fn chksum_with<H>(&mut self, hash: &mut H) -> Result<()>
322 where
323 H: Hash,
324 {
325 if self.is_terminal() {
326 return Err(Error::IsTerminal);
327 }
328
329 loop {
330 let buffer = self.fill_buf()?;
331 let length = buffer.len();
332 if length == 0 {
333 break;
334 }
335 buffer.hash_with(hash);
336 self.consume(length);
337 }
338 Ok(())
339 }
340});
341
342#[cfg(feature = "async-runtime-tokio")]
344#[async_trait]
345pub trait AsyncChksumable: Send {
346 async fn chksum<H>(&mut self) -> Result<H::Digest>
348 where
349 H: Hash + Send,
350 {
351 let mut hash = H::default();
352 self.chksum_with(&mut hash).await?;
353 Ok(hash.digest())
354 }
355
356 async fn chksum_with<H>(&mut self, hash: &mut H) -> Result<()>
358 where
359 H: Hash + Send;
360}
361
362#[cfg(feature = "async-runtime-tokio")]
363#[async_trait]
364impl<T> AsyncChksumable for T
365where
366 T: Hashable + Send,
367{
368 async fn chksum_with<H>(&mut self, hash: &mut H) -> Result<()>
369 where
370 H: Hash + Send,
371 {
372 self.hash_with(hash);
373 Ok(())
374 }
375}