1use std::path::{Path, PathBuf};
2
3use async_trait::async_trait;
4use tokio::fs::{metadata, read_dir, DirEntry, File, ReadDir};
5use tokio::io::{AsyncBufReadExt as _, BufReader, Stdin};
6
7use crate::{AsyncChksumable, Hash, Hashable, Result};
8
9macro_rules! impl_async_chksumable {
10 ($($t:ty),+ => $i:tt) => {
11 $(
12 #[async_trait]
13 impl AsyncChksumable for $t $i
14 )*
15 };
16}
17
18impl_async_chksumable!(Path, &Path, &mut Path => {
19 async fn chksum_with<H>(&mut self, hash: &mut H) -> Result<()>
20 where
21 H: Hash + Send,
22 {
23 let metadata = metadata(&self).await?;
24 if metadata.is_dir() {
25 read_dir(self).await?.chksum_with(hash).await
26 } else {
27 File::open(self).await?.chksum_with(hash).await
29 }
30 }
31
32});
33
34impl_async_chksumable!(PathBuf, &PathBuf, &mut PathBuf => {
35 async fn chksum_with<H>(&mut self, hash: &mut H) -> Result<()>
36 where
37 H: Hash + Send,
38 {
39 self.as_path().chksum_with(hash).await
40 }
41});
42
43impl_async_chksumable!(File, &mut File => {
45 async fn chksum_with<H>(&mut self, hash: &mut H) -> Result<()>
46 where
47 H: Hash + Send,
48 {
49 let mut reader = BufReader::new(self);
55 loop {
56 let buffer = reader.fill_buf().await?;
57 let length = buffer.len();
58 if length == 0 {
59 break;
60 }
61 buffer.hash_with(hash);
62 reader.consume(length);
63 }
64 Ok(())
65 }
66});
67
68impl_async_chksumable!(DirEntry, &DirEntry, &mut DirEntry => {
69 async fn chksum_with<H>(&mut self, hash: &mut H) -> Result<()>
70 where
71 H: Hash + Send,
72 {
73 self.path().chksum_with(hash).await
74 }
75});
76
77impl_async_chksumable!(ReadDir, &mut ReadDir => {
78 async fn chksum_with<H>(&mut self, hash: &mut H) -> Result<()>
79 where
80 H: Hash + Send,
81 {
82 let mut dir_entries = Vec::new();
83 while let Some(dir_entry) = self.next_entry().await? {
84 dir_entries.push(dir_entry);
85 }
86 dir_entries.sort_by_key(DirEntry::path);
87 for mut dir_entry in dir_entries {
88 dir_entry.chksum_with(hash).await?;
89 }
90 Ok(())
91 }
92});
93
94impl_async_chksumable!(Stdin, &mut Stdin => {
96 async fn chksum_with<H>(&mut self, hash: &mut H) -> Result<()>
97 where
98 H: Hash + Send,
99 {
100 let mut reader = BufReader::new(self);
106 loop {
107 let buffer = reader.fill_buf().await?;
108 let length = buffer.len();
109 if length == 0 {
110 break;
111 }
112 buffer.hash_with(hash);
113 reader.consume(length);
114 }
115 Ok(())
116 }
117});