Skip to main content

pray_core/
package_spec_hash.rs

1use super::PackageSpec;
2use crate::hashing::sha256_prefixed;
3use crate::{PrayError, PrayResult};
4use std::collections::BTreeMap;
5
6impl PackageSpec {
7    pub fn canonicalized(&self) -> Self {
8        let mut package = self.clone();
9        package.files.sort();
10        package.authors.sort();
11        package.targets.sort();
12        package.dependencies.sort_by(|left, right| {
13            left.name
14                .cmp(&right.name)
15                .then(left.constraint.cmp(&right.constraint))
16                .then(left.optional.cmp(&right.optional))
17        });
18        package
19    }
20
21    pub fn tree_hash_for_root(&self, root: &std::path::Path) -> PrayResult<String> {
22        let mut file_bytes = BTreeMap::new();
23        for file in &self.files {
24            let path = root.join(file);
25            if !path.exists() {
26                return Err(PrayError::Integrity(format!(
27                    "package file missing: {file}"
28                )));
29            }
30            if path.is_dir() {
31                return Err(PrayError::Integrity(format!(
32                    "package file is a directory: {file}"
33                )));
34            }
35            file_bytes.insert(file.clone(), std::fs::read(&path)?);
36        }
37        Self::tree_hash_from_file_bytes(&file_bytes)
38    }
39
40    pub fn tree_hash_from_file_bytes(file_bytes: &BTreeMap<String, Vec<u8>>) -> PrayResult<String> {
41        let mut entries = file_bytes
42            .iter()
43            .map(|(path, bytes)| (path.clone(), sha256_prefixed(bytes)))
44            .collect::<Vec<_>>();
45        entries.sort_by(|left, right| left.0.cmp(&right.0));
46
47        let mut serialized = String::new();
48        for (path, hash) in entries {
49            serialized.push_str("file\0regular\0");
50            serialized.push_str(&path);
51            serialized.push('\0');
52            serialized.push_str(&hash);
53            serialized.push('\n');
54        }
55        Ok(sha256_prefixed(serialized.as_bytes()))
56    }
57}