1use std::fs::File;
18use std::io::{BufRead, BufReader, Error};
19use ring::digest as r_digest;
20
21
22#[derive(Debug, Eq, PartialEq)]
24pub enum Hashing {
25 Sha1,
27 Sha256,
29 Sha384,
31 Sha512,
33 Sha512_256,
35}
36
37impl Hashing {
38
39 pub fn new_context(&self) -> HashContext {
41 match self {
42 Self::Sha1 => HashContext(r_digest::Context::new(
43 &r_digest::SHA1_FOR_LEGACY_USE_ONLY)),
44 Self::Sha256 => HashContext(r_digest::Context::new(
45 &r_digest::SHA256)),
46 Self::Sha384 => HashContext(r_digest::Context::new(
47 &r_digest::SHA384)),
48 Self::Sha512 => HashContext(r_digest::Context::new(
49 &r_digest::SHA512)),
50 Self::Sha512_256 => HashContext(r_digest::Context::new(
51 &r_digest::SHA512_256)),
52 }
53 }
54
55 pub fn hash(&self, data: &[u8]) -> Hash {
57 let mut ctx = self.new_context();
58 ctx.update(data);
59 ctx.finish()
60 }
61
62 #[inline]
64 pub fn hash_vec(&self, data: Vec<u8>) -> Hash {
65 self.hash(data.as_ref())
66 }
67
68 #[inline]
70 pub fn hash_str(&self, data: &str) -> Hash {
71 self.hash(data.as_ref())
72 }
73
74 pub fn hash_file(&self, path: &str) -> Result<Hash, Error> {
77 let file = File::open(path)?;
78 let reader = BufReader::new(file);
79
80 let mut ctx = self.new_context();
81 for line in reader.lines() {
82 ctx.update(line.unwrap().as_bytes());
83 }
84
85 Ok(ctx.finish())
86 }
87
88}
89
90
91
92#[derive(Clone)]
95pub struct HashContext(r_digest::Context);
96
97impl HashContext {
98
99 #[inline]
101 pub fn update(&mut self, data: &[u8]) {
102 self.0.update(data);
103 }
104
105 #[inline]
108 pub fn finish(self) -> Hash {
109 Hash(self.0.finish())
110 }
111
112}
113
114
115#[derive(Clone, Copy)]
118pub struct Hash(r_digest::Digest);
119
120impl Hash {
121
122 #[inline]
124 pub fn as_bytes(&self) -> &[u8] {
125 self.0.as_ref()
126 }
127
128 #[inline]
130 pub fn to_vec(&self) -> Vec<u8> {
131 self.as_bytes().to_vec()
132 }
133
134 #[inline]
136 pub fn to_hex(&self) -> String {
137 hex::encode(self.as_bytes())
138 }
139
140}
141
142
143#[cfg(test)]
145mod tests {
146
147 use super::*;
148 use crate::Hashing::{Sha1, Sha256, Sha384, Sha512, Sha512_256};
149 use std::fs::File;
150 use std::io::Write;
151 use hex::ToHex;
152
153 const DATA_TO_DIGEST: &[u8] = b"Hello, World!";
154 const FILE_NAME: &str = "testfile.txt";
155
156 fn create_test_file() {
157 let mut file = File::create("testfile.txt").unwrap();
159 file.write_all(DATA_TO_DIGEST).unwrap();
160 file.sync_all().unwrap();
161 }
162
163 #[test]
164 fn sha256_context() {
165 let mut r_ctx = r_digest::Context::new(&r_digest::SHA256);
166 r_ctx.update(DATA_TO_DIGEST);
167 let expected = r_ctx.finish();
168
169 let mut ctx = Sha256.new_context();
170 ctx.update(DATA_TO_DIGEST);
171 let result = ctx.finish();
172
173 assert_eq!(result.as_bytes(), expected.as_ref());
174 assert_eq!(result.to_vec(), expected.as_ref().to_vec());
175 assert_eq!(result.to_hex(), expected.encode_hex::<String>());
176 }
177
178 #[test]
179 fn sha1_digest() {
180 let expected = r_digest::digest(&r_digest::SHA1_FOR_LEGACY_USE_ONLY, DATA_TO_DIGEST);
181 let result = Sha1.hash(DATA_TO_DIGEST);
182
183 assert_eq!(result.as_bytes(), expected.as_ref());
184 assert_eq!(result.to_vec(), expected.as_ref().to_vec());
185 assert_eq!(result.to_hex(), expected.encode_hex::<String>());
186 }
187
188 #[test]
189 fn sha384_digest() {
190 let expected = r_digest::digest(&r_digest::SHA384, DATA_TO_DIGEST);
191 let result = Sha384.hash(DATA_TO_DIGEST);
192
193 assert_eq!(result.as_bytes(), expected.as_ref());
194 assert_eq!(result.to_vec(), expected.as_ref().to_vec());
195 assert_eq!(result.to_hex(), expected.encode_hex::<String>());
196 }
197
198 #[test]
199 fn sha512_digest() {
200 let expected = r_digest::digest(&r_digest::SHA512, DATA_TO_DIGEST);
201 let result = Sha512.hash(DATA_TO_DIGEST);
202
203 assert_eq!(result.as_bytes(), expected.as_ref());
204 assert_eq!(result.to_vec(), expected.as_ref().to_vec());
205 assert_eq!(result.to_hex(), expected.encode_hex::<String>());
206 }
207
208 #[test]
209 fn sha512_256_digest() {
210 let expected = r_digest::digest(&r_digest::SHA512_256, DATA_TO_DIGEST);
211 let result = Sha512_256.hash(DATA_TO_DIGEST);
212
213 assert_eq!(result.as_bytes(), expected.as_ref());
214 assert_eq!(result.to_vec(), expected.as_ref().to_vec());
215 assert_eq!(result.to_hex(), expected.encode_hex::<String>());
216 }
217
218 #[test]
219 fn sha256_digest() {
220 let expected = r_digest::digest(&r_digest::SHA256, DATA_TO_DIGEST);
221 let result = Sha256.hash(DATA_TO_DIGEST);
222
223 assert_eq!(result.as_bytes(), expected.as_ref());
224 assert_eq!(result.to_vec(), expected.as_ref().to_vec());
225 assert_eq!(result.to_hex(), expected.encode_hex::<String>());
226 }
227
228 #[test]
229 fn sha256_digest_vec() {
230 let expected = r_digest::digest(&r_digest::SHA256, DATA_TO_DIGEST);
231 let result = Sha256.hash_vec(DATA_TO_DIGEST.to_vec());
232
233 assert_eq!(result.as_bytes(), expected.as_ref());
234 assert_eq!(result.to_vec(), expected.as_ref().to_vec());
235 assert_eq!(result.to_hex(), expected.encode_hex::<String>());
236 }
237
238 #[test]
239 fn sha256_digest_str() {
240 let expected = r_digest::digest(&r_digest::SHA256, DATA_TO_DIGEST);
241 let result = Sha256.hash_str(&String::from_utf8(DATA_TO_DIGEST.to_vec()).unwrap());
242
243 assert_eq!(result.as_bytes(), expected.as_ref());
244 assert_eq!(result.to_vec(), expected.as_ref().to_vec());
245 assert_eq!(result.to_hex(), expected.encode_hex::<String>());
246 }
247
248 #[test]
249 fn sha256_digest_file() {
250 create_test_file();
251
252 let expected = r_digest::digest(&r_digest::SHA256, DATA_TO_DIGEST);
253 let result = Sha256.hash_file(FILE_NAME).unwrap();
254
255 assert_eq!(result.as_bytes(), expected.as_ref());
256 assert_eq!(result.to_vec(), expected.as_ref().to_vec());
257 assert_eq!(result.to_hex(), expected.encode_hex::<String>());
258 }
259
260 #[test]
261 fn sha256_digest_file_error() {
263 let result = Sha256.hash_file("notfound.txt");
264 let e = match result {
265 Ok(_) => panic!("Test should fail"),
266 Err(e) => e
267 };
268 assert_eq!(e.to_string(), "No such file or directory (os error 2)");
269 }
270
271}