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…
⎇ 8bf1623044075274682e4351a0f8e120047c2bed
crates/hammer-core/src/hash.rs
History
Who touched what
raw
Scaffold inicial: workspace Rust + SDDs completos
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}