use serde::{Deserialize, Serialize};
#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct StoreHash(pub String);
impl StoreHash {
pub fn of<T: Serialize>(value: &T) -> Self {
Self(tatara_lisp::hash::hex_blake3_of_json(value))
}
pub fn short(&self) -> &str {
&self.0[..20.min(self.0.len())]
}
}
impl std::fmt::Display for StoreHash {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(&self.0)
}
}
#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct StorePath {
pub hash: StoreHash,
pub name: String,
pub version: Option<String>,
}
impl StorePath {
pub fn new(hash: StoreHash, name: impl Into<String>, version: Option<String>) -> Self {
Self {
hash,
name: name.into(),
version,
}
}
pub fn render(&self) -> String {
match &self.version {
Some(v) => format!("{}-{}-{}", self.hash.short(), self.name, v),
None => format!("{}-{}", self.hash.short(), self.name),
}
}
}
impl std::fmt::Display for StorePath {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(&self.render())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn hash_is_deterministic_and_64_hex() {
let a = StoreHash::of(&"hello");
let b = StoreHash::of(&"hello");
assert_eq!(a, b);
assert_eq!(a.0.len(), 64);
}
#[test]
fn hash_differs_by_content() {
assert_ne!(StoreHash::of(&"a"), StoreHash::of(&"b"));
}
#[test]
fn store_path_renders_with_version() {
let p = StorePath::new(StoreHash::of(&"x"), "hello", Some("2.12".into()));
let rendered = p.render();
assert!(rendered.ends_with("-hello-2.12"));
}
#[test]
fn store_hash_of_matches_pre_lift_hand_authored_chain_bytewise() {
#[derive(Serialize)]
struct Fixture {
name: String,
n: u32,
}
for value in [
Fixture {
name: "hello".into(),
n: 0,
},
Fixture {
name: "coreutils".into(),
n: 9,
},
Fixture {
name: String::new(),
n: u32::MAX,
},
] {
let pre_lift = {
let bytes = serde_json::to_vec(&value).unwrap_or_default();
hex::encode(blake3::hash(&bytes).as_bytes())
};
assert_eq!(
StoreHash::of(&value).0,
pre_lift,
"StoreHash::of drifted from pre-lift hand-authored chain for {:?}",
value.name,
);
}
}
#[test]
fn store_hash_of_body_matches_substrate_owner_bytewise() {
#[derive(Serialize)]
struct Fixture {
k: &'static str,
}
let v = Fixture { k: "same" };
assert_eq!(
StoreHash::of(&v).0,
tatara_lisp::hash::hex_blake3_of_json(&v)
);
}
}