winget_types/shared/
sha_256.rs

1use core::fmt;
2
3use heapless::String;
4use sha2::{Sha256, digest::Output};
5
6// 256 bits / 4 bits per hex character
7const SHA256_LEN: usize = 256 / 0xF_u8.count_ones() as usize;
8
9#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
10#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
11pub struct Sha256String(String<SHA256_LEN>);
12
13impl Sha256String {
14    /// Creates a `Sha256String` from a Sha256 digest.
15    ///
16    /// # Examples
17    ///
18    /// ```
19    /// use winget_types::{sha2::{Digest, Sha256}, Sha256String};
20    ///
21    /// // Digest some data manually
22    /// let sha256_digest = Sha256::digest("abc");
23    ///
24    /// assert_eq!(
25    ///     Sha256String::from_digest(&sha256_digest).as_str(),
26    ///     "BA7816BF8F01CFEA414140DE5DAE2223B00361A396177A9CB410FF61F20015AD"
27    /// );
28    /// ```
29    #[must_use]
30    pub fn from_digest(digest: &Output<Sha256>) -> Self {
31        let mut encode_buf = [0; SHA256_LEN];
32
33        Self(
34            base16ct::upper::encode_str(digest, &mut encode_buf)
35                .unwrap_or_else(|_| unreachable!("SHA256 digests should always be 32 bytes long"))
36                .parse::<String<SHA256_LEN>>()
37                .unwrap_or_else(|_| {
38                    unreachable!("Sha256 hashes should always be {SHA256_LEN} bytes long")
39                }),
40        )
41    }
42
43    /// Creates a `Sha256String` by hashing data from a reader.
44    ///
45    /// This will repeatedly read the data into a buffer of length 4096.
46    ///
47    /// # Errors
48    ///
49    /// Returns the propagated `Err` from [`io::read`].
50    ///
51    /// # Examples
52    ///
53    /// [`File`]s implement `Read`:
54    ///
55    /// [`File`]: std::fs::File
56    /// [`io::read`]: std::io::Read::read
57    ///
58    /// ```no_run
59    /// use std::io;
60    /// use std::fs::File;
61    ///
62    /// use winget_types::Sha256String;
63    ///
64    /// fn main() -> io::Result<()> {
65    ///     let mut f = File::open("foo.txt")?;
66    ///
67    ///     let sha256_string = Sha256String::hash_from_reader(f)?;
68    ///
69    ///     println!("File SHA256 hash: {sha256_string}");
70    ///     Ok(())
71    /// }
72    /// ```
73    #[cfg(feature = "std")]
74    pub fn hash_from_reader<R: std::io::Read>(mut reader: R) -> std::io::Result<Self> {
75        use sha2::{Digest, Sha256};
76
77        let mut hasher = Sha256::new();
78        let mut buffer = [0; 1 << 12];
79
80        loop {
81            let count = reader.read(&mut buffer)?;
82            if count == 0 {
83                break;
84            }
85            hasher.update(&buffer[..count]);
86        }
87
88        Ok(Self::from_digest(&hasher.finalize()))
89    }
90
91    /// Extracts a string slice containing the entire `Sha256String`.
92    #[must_use]
93    #[inline]
94    pub fn as_str(&self) -> &str {
95        self.0.as_str()
96    }
97}
98
99impl Default for Sha256String {
100    fn default() -> Self {
101        Self(core::iter::repeat_n('0', SHA256_LEN).collect::<_>())
102    }
103}
104
105impl fmt::Display for Sha256String {
106    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
107        self.0.fmt(f)
108    }
109}