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