chksum_core/
tokio.rs

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            // everything treat as a file when it is not a directory
28            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
43// TODO: missing `&File` implementation
44impl_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        // TODO: tracking issue [tokio-rs/tokio#6407](github.com/tokio-rs/tokio/issues/6407)
50        // if self.is_terminal() {
51        //     return Err(Error::IsTerminal);
52        // }
53
54        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
94// TODO: missing `&Stdin` implementation
95impl_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        // TODO: tracking issue [tokio-rs/tokio#6407](github.com/tokio-rs/tokio/issues/6407)
101        // if self.is_terminal() {
102        //     return Err(Error::IsTerminal);
103        // }
104
105        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});