pub fn normalize(text: &str) -> String {
text.replace("\r\n", "\n").replace('\r', "\n")
}
pub fn content_hash(text: &str) -> String {
use sha2::Digest;
let normalized = normalize(text);
let mut hasher = sha2::Sha256::new();
hasher.update(normalized.as_bytes());
let digest = hasher.finalize();
format!("sha256:{}", hex::encode(digest))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn crlf_と_lf_は同じハッシュになる() {
let lf = "line one\nline two\n";
let crlf = "line one\r\nline two\r\n";
assert_eq!(content_hash(lf), content_hash(crlf));
}
#[test]
fn content_hash_はsha256プレフィックス付きの小文字hexを返す() {
let hash = content_hash("hello");
assert!(hash.starts_with("sha256:"), "unexpected hash: {hash}");
let hex = hash.strip_prefix("sha256:").unwrap();
assert_eq!(hex.len(), 64);
assert!(hex
.chars()
.all(|c| c.is_ascii_hexdigit() && !c.is_ascii_uppercase()));
assert_ne!(content_hash("hello"), content_hash("hello "));
}
#[test]
fn 単独crもlfと同じハッシュになる() {
let lf = "a\nb";
let cr = "a\rb";
assert_eq!(content_hash(lf), content_hash(cr));
}
}