1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
//! This crate provides a convenient interface for calculating hash digests on the fly while reading data from a reader. It supports various hash algorithms, and the library is designed to be easy to use.
//!
//! # Setup
//!
//! To use this crate, add the following entry to your `Cargo.toml` file in the `dependencies` section:
//!
//! ```toml
//! [dependencies]
//! chksum-reader = "0.0.0"
//! ```
//!
//! Alternatively, you can use the [`cargo add`](https://doc.rust-lang.org/cargo/commands/cargo-add.html) subcommand:
//!
//! ```sh
//! cargo add chksum-reader
//! ```     
//!
//! # Usage
//!
//! ```rust,ignore
//! use std::io::{self, Read};
//!
//! use chksum_md5::MD5;
//! use chksum_reader::Reader;
//!
//! fn main() -> io::Result<()> {
//!     // Create a new reader with the MD5 hash algorithm
//!     let mut reader = Reader::<_, MD5>::new(io::stdin());
//!
//!     // Read data from the reader
//!     let mut buffer = Vec::new();
//!     reader.read_to_end(&mut buffer)?;
//!
//!     // Get the calculated digest
//!     let digest = reader.digest();
//!
//!     // Print the digest (hex representation)
//!     println!("Digest: {}", digest.to_hex_lowercase());
//!
//!     Ok(())
//! }
//! ```
//!
//! # Implementations
//!
//! This crate should be used along with a hash implementation crate.
//!  
//! Various crates implement their own [`Reader`], which can be enabled with the `reader` Cargo feature.
//!
//! # License
//!
//! This crate is licensed under the MIT License.

#![forbid(unsafe_code)]

use std::io::{self, BufRead, Read};

use chksum_core::Hash;

/// Creates new [`Reader`].
pub fn new<R, H>(inner: R) -> Reader<R, H>
where
    R: Read,
    H: Hash,
{
    Reader::new(inner)
}

/// Creates new [`Reader`] with provided hash.
pub fn with_hash<R, H>(inner: R, hash: H) -> Reader<R, H>
where
    R: Read,
    H: Hash,
{
    Reader::with_hash(inner, hash)
}

/// Wraps a reader and calculates the hash digest on the fly.
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Reader<R, H>
where
    R: Read,
    H: Hash,
{
    inner: R,
    hash: H,
}

impl<R, H> Reader<R, H>
where
    R: Read,
    H: Hash,
{
    /// Creates new [`Reader`].
    pub fn new(inner: R) -> Self {
        let hash = H::default();
        Self::with_hash(inner, hash)
    }

    /// Creates new [`Reader`] with provided hash.
    #[must_use]
    pub const fn with_hash(inner: R, hash: H) -> Self {
        Self { inner, hash }
    }

    /// Unwraps this [`Reader`], returning the underlying reader.
    #[must_use]
    pub fn into_inner(self) -> R {
        let Self { inner, .. } = self;
        inner
    }

    /// Returns calculated hash digest.
    #[must_use]
    pub fn digest(&self) -> H::Digest {
        self.hash.digest()
    }
}

impl<R, H> Read for Reader<R, H>
where
    R: Read,
    H: Hash,
{
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        let n = self.inner.read(buf)?;
        self.hash.update(&buf[..n]);
        Ok(n)
    }
}

impl<R, H> BufRead for Reader<R, H>
where
    R: BufRead,
    H: Hash,
{
    fn consume(&mut self, amt: usize) {
        self.inner.consume(amt);
    }

    fn fill_buf(&mut self) -> io::Result<&[u8]> {
        self.inner.fill_buf()
    }
}