1use std::fmt;
2
3use sha2::{Digest, Sha256};
4
5#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
6pub struct Fingerprint(pub [u8; 32]);
7
8impl Fingerprint {
9 pub fn of_mod(manifest: &[u8], component: &[u8]) -> Self {
10 let mut hash = Sha256::new();
11 hash.update(b"pfx-mod 1\0");
12 hash.update((manifest.len() as u64).to_le_bytes());
13 hash.update(manifest);
14 hash.update((component.len() as u64).to_le_bytes());
15 hash.update(component);
16 Self(hash.finalize().into())
17 }
18
19 pub fn of_set<'a>(mods: impl IntoIterator<Item = (&'a str, Fingerprint)>) -> Self {
20 let mut mods: Vec<(&str, Fingerprint)> = mods.into_iter().collect();
21 mods.sort();
22 let mut hash = Sha256::new();
23 hash.update(b"pfx-mod set 1\0");
24 hash.update((mods.len() as u64).to_le_bytes());
25 for (id, fingerprint) in mods {
26 hash.update((id.len() as u64).to_le_bytes());
27 hash.update(id.as_bytes());
28 hash.update(fingerprint.0);
29 }
30 Self(hash.finalize().into())
31 }
32
33 pub fn hex(&self) -> String {
34 self.0.iter().map(|b| format!("{b:02x}")).collect()
35 }
36}
37
38impl fmt::Display for Fingerprint {
39 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
40 f.write_str(&self.hex())
41 }
42}