pray-core 1.13.0

Core library for Prayfile, the package manager for the language placed before inference
Documentation
use super::PackageSpec;
use crate::hashing::sha256_prefixed;
use crate::{PrayError, PrayResult};
use std::collections::BTreeMap;

impl PackageSpec {
    pub fn canonicalized(&self) -> Self {
        let mut package = self.clone();
        package.files.sort();
        package.authors.sort();
        package.targets.sort();
        package.dependencies.sort_by(|left, right| {
            left.name
                .cmp(&right.name)
                .then(left.constraint.cmp(&right.constraint))
                .then(left.optional.cmp(&right.optional))
        });
        package
    }

    pub fn tree_hash_for_root(&self, root: &std::path::Path) -> PrayResult<String> {
        let mut file_bytes = BTreeMap::new();
        for file in &self.files {
            let path = root.join(file);
            if !path.exists() {
                return Err(PrayError::Integrity(format!(
                    "package file missing: {file}"
                )));
            }
            if path.is_dir() {
                return Err(PrayError::Integrity(format!(
                    "package file is a directory: {file}"
                )));
            }
            file_bytes.insert(file.clone(), std::fs::read(&path)?);
        }
        Self::tree_hash_from_file_bytes(&file_bytes)
    }

    pub fn tree_hash_from_file_bytes(file_bytes: &BTreeMap<String, Vec<u8>>) -> PrayResult<String> {
        let mut entries = file_bytes
            .iter()
            .map(|(path, bytes)| (path.clone(), sha256_prefixed(bytes)))
            .collect::<Vec<_>>();
        entries.sort_by(|left, right| left.0.cmp(&right.0));

        let mut serialized = String::new();
        for (path, hash) in entries {
            serialized.push_str("file\0regular\0");
            serialized.push_str(&path);
            serialized.push('\0');
            serialized.push_str(&hash);
            serialized.push('\n');
        }
        Ok(sha256_prefixed(serialized.as_bytes()))
    }
}