gioser

sergio/takana

> Una distribución Linux construida con un laboratorio funcional y hermético en el > sótano, y una terminal mutable, clásica y anárquica en el piso de arriba — diseñada > desde el suelo para que una IA programadora entienda, modifique y co…

TOML 815Rust 56Shell 37Markdown 291229 Definiciones

⎇ 8bf1623044075274682e4351a0f8e120047c2bed crates/hammer-core/src/hash.rs Rust · 66 líneas · 2.2 KB Historia Quién tocó qué crudo

Scaffold inicial: workspace Rust + SDDs completos 8bf162304 · sergio ·

1//! Direccionamiento por contenido (CAS). Ver `docs/02-build-lab.md` §2.
2//!
3//! El `ArtifactHash` identifica un artefacto por TODO lo que influye en su salida:
4//! commit fuente + parches + flags/compilador/target + hashes de dependencias.
5
6use serde::{Deserialize, Serialize};
7
8/// Hash BLAKE3 de un artefacto, con prefijo legible `b3:`.
9#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
10pub struct ArtifactHash(String);
11
12impl ArtifactHash {
13    /// Construye desde bytes ya hasheados (representación hex).
14    pub fn from_hex(hex: impl Into<String>) -> Self {
15        ArtifactHash(format!("b3:{}", hex.into()))
16    }
17
18    /// Hashea un conjunto ordenado de entradas. El llamador es responsable de pasar las
19    /// entradas en orden canónico y estable (ver `docs/02-build-lab.md` §2).
20    pub fn of_inputs(inputs: &[&[u8]]) -> Self {
21        let mut hasher = blake3::Hasher::new();
22        for chunk in inputs {
23            // length-prefijado para evitar colisiones por concatenación ambigua.
24            hasher.update(&(chunk.len() as u64).to_le_bytes());
25            hasher.update(chunk);
26        }
27        ArtifactHash(format!("b3:{}", hasher.finalize().to_hex()))
28    }
29
30    /// Forma corta para directorios del store: `<hash-sin-prefijo>-<name>`.
31    pub fn store_dir_name(&self, name: &str) -> String {
32        let bare = self.0.strip_prefix("b3:").unwrap_or(&self.0);
33        format!("{bare}-{name}")
34    }
35
36    pub fn as_str(&self) -> &str {
37        &self.0
38    }
39}
40
41impl std::fmt::Display for ArtifactHash {
42    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
43        f.write_str(&self.0)
44    }
45}
46
47#[cfg(test)]
48mod tests {
49    use super::*;
50
51    #[test]
52    fn deterministic_and_order_sensitive() {
53        let a = ArtifactHash::of_inputs(&[b"grep", b"abc123", b"--static"]);
54        let b = ArtifactHash::of_inputs(&[b"grep", b"abc123", b"--static"]);
55        assert_eq!(a, b, "misma entrada ⇒ mismo hash");
56
57        let c = ArtifactHash::of_inputs(&[b"abc123", b"grep", b"--static"]);
58        assert_ne!(a, c, "orden distinto ⇒ hash distinto");
59    }
60
61    #[test]
62    fn store_dir_name_strips_prefix() {
63        let h = ArtifactHash::from_hex("deadbeef");
64        assert_eq!(h.store_dir_name("grep"), "deadbeef-grep");
65    }
66}