pg_embedded_setup_unpriv/extensions/
digest.rs1use std::{fmt, io};
4
5use sha2::{Digest, Sha256};
6
7#[derive(Debug, Clone, PartialEq, Eq, Hash)]
22pub struct Sha256Hex(String);
23
24#[derive(Debug, Clone, PartialEq, Eq)]
26pub struct InvalidDigest {
27 pub value: String,
29}
30
31impl fmt::Display for InvalidDigest {
32 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
33 write!(
34 f,
35 "digest {:?} must be 64 lower-case hex characters",
36 self.value
37 )
38 }
39}
40
41impl std::error::Error for InvalidDigest {}
42
43impl Sha256Hex {
44 pub fn parse(value: &str) -> Result<Self, InvalidDigest> {
51 let is_valid = value.len() == 64
52 && value
53 .bytes()
54 .all(|byte| byte.is_ascii_digit() || (b'a'..=b'f').contains(&byte));
55 if is_valid {
56 Ok(Self(value.to_owned()))
57 } else {
58 Err(InvalidDigest {
59 value: value.chars().take(80).collect(),
60 })
61 }
62 }
63
64 #[must_use]
66 pub fn of_bytes(bytes: &[u8]) -> Self { Self::from_hasher(Sha256::new_with_prefix(bytes)) }
67
68 pub fn of_reader<R: io::Read>(mut reader: R) -> io::Result<Self> {
74 let mut hasher = Sha256::new();
75 let mut buffer = vec![0_u8; 64 * 1024];
76 loop {
77 let count = reader.read(&mut buffer)?;
78 if count == 0 {
79 break;
80 }
81 hasher.update(buffer.get(..count).unwrap_or_default());
82 }
83 Ok(Self::from_hasher(hasher))
84 }
85
86 pub fn of_file(path: &camino::Utf8Path) -> io::Result<Self> {
92 Self::of_reader(std::fs::File::open(path)?)
93 }
94
95 fn from_hasher(hasher: Sha256) -> Self {
97 let bytes = hasher.finalize();
98 let mut encoded = String::with_capacity(bytes.len() * 2);
99 for byte in bytes {
100 encoded.push(lower_hex_digit(byte >> 4));
101 encoded.push(lower_hex_digit(byte & 0x0f));
102 }
103 Self(encoded)
104 }
105
106 #[must_use]
108 pub fn as_str(&self) -> &str { &self.0 }
109}
110
111impl AsRef<str> for Sha256Hex {
112 fn as_ref(&self) -> &str { &self.0 }
113}
114
115impl fmt::Display for Sha256Hex {
116 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str(&self.0) }
117}
118
119const fn lower_hex_digit(nibble: u8) -> char {
121 if nibble < 10 {
122 (b'0' + nibble) as char
123 } else {
124 (b'a' + nibble - 10) as char
125 }
126}
127
128pub(super) struct HashingWriter<W> {
130 inner: W,
131 hasher: Sha256,
132 written: u64,
133}
134
135impl<W: io::Write> HashingWriter<W> {
136 pub(super) fn new(inner: W) -> Self {
138 Self {
139 inner,
140 hasher: Sha256::new(),
141 written: 0,
142 }
143 }
144
145 pub(super) fn finish(self) -> (W, Sha256Hex, u64) {
147 (
148 self.inner,
149 Sha256Hex::from_hasher(self.hasher),
150 self.written,
151 )
152 }
153}
154
155impl<W: io::Write> io::Write for HashingWriter<W> {
156 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
157 let count = self.inner.write(buf)?;
158 self.hasher.update(buf.get(..count).unwrap_or_default());
159 self.written += count as u64;
160 Ok(count)
161 }
162
163 fn flush(&mut self) -> io::Result<()> { self.inner.flush() }
164}