async_fetcher/
checksum.rs1use digest::{generic_array::GenericArray, Digest, OutputSizeUser};
5use hex::FromHex;
6use md5::Md5;
7use serde::Deserialize;
8use sha2::Sha256;
9use std::{convert::TryFrom, io};
10
11#[derive(Debug, Clone)]
13pub enum Checksum {
14 Md5(GenericArray<u8, <Md5 as OutputSizeUser>::OutputSize>),
15 Sha256(GenericArray<u8, <Sha256 as OutputSizeUser>::OutputSize>),
16}
17
18#[derive(Debug, Error)]
20pub enum ChecksumError {
21 #[error("expected {}, found {}", _0, _1)]
22 Invalid(String, String),
23 #[error("I/O error encountered while reading from reader")]
24 IO(#[from] io::Error),
25}
26
27pub enum SumStr<'a> {
29 Md5(&'a str),
30 Sha256(&'a str),
31}
32
33#[derive(Deserialize)]
35pub enum SumStrBuf {
36 Md5(String),
37 Sha256(String),
38}
39
40impl SumStrBuf {
41 pub fn as_ref(&'_ self) -> SumStr<'_> {
42 match self {
43 SumStrBuf::Md5(string) => SumStr::Md5(string.as_str()),
44 SumStrBuf::Sha256(string) => SumStr::Sha256(string.as_str()),
45 }
46 }
47}
48
49impl<'a> TryFrom<SumStr<'a>> for Checksum {
50 type Error = hex::FromHexError;
51
52 fn try_from(input: SumStr) -> Result<Self, Self::Error> {
53 match input {
54 SumStr::Md5(sum) => <[u8; 16]>::from_hex(sum)
55 .map(GenericArray::from)
56 .map(Checksum::Md5),
57 SumStr::Sha256(sum) => <[u8; 32]>::from_hex(sum)
58 .map(GenericArray::from)
59 .map(Checksum::Sha256),
60 }
61 }
62}
63
64impl Checksum {
65 pub fn validate<F: std::io::Read>(
66 &self,
67 reader: F,
68 buffer: &mut [u8],
69 ) -> Result<(), ChecksumError> {
70 match self {
71 Checksum::Md5(sum) => checksum::<Md5, F>(reader, buffer, sum),
72 Checksum::Sha256(sum) => checksum::<Sha256, F>(reader, buffer, sum),
73 }
74 }
75}
76
77pub(crate) fn checksum<D: Digest, F: io::Read>(
78 reader: F,
79 buffer: &mut [u8],
80 expected: &GenericArray<u8, D::OutputSize>,
81) -> Result<(), ChecksumError> {
82 let result = generate_checksum::<D, F>(reader, buffer).map_err(ChecksumError::IO)?;
83
84 if result == *expected {
85 Ok(())
86 } else {
87 Err(ChecksumError::Invalid(
88 hex::encode(expected),
89 hex::encode(result),
90 ))
91 }
92}
93
94pub(crate) fn generate_checksum<D: Digest, F: io::Read>(
95 mut reader: F,
96 buffer: &mut [u8],
97) -> io::Result<GenericArray<u8, D::OutputSize>> {
98 let mut hasher = D::new();
99 let mut read;
100
101 loop {
102 read = reader.read(buffer)?;
103
104 if read == 0 {
105 return Ok(hasher.finalize());
106 }
107
108 hasher.update(&buffer[..read]);
109 }
110}