use std::path::Path;
pub(super) fn sha256_hex(bytes: &[u8]) -> String {
use std::fmt::Write as _;
let digest = ring::digest::digest(&ring::digest::SHA256, bytes);
let mut out = String::with_capacity(64);
for byte in digest.as_ref() {
let _ = write!(out, "{byte:02x}");
}
out
}
pub(super) fn parse_sha256sums(text: &str, file: &str) -> Option<String> {
for line in text.lines() {
let line = line.trim();
if line.is_empty() || line.starts_with('#') {
continue;
}
let Some((hash, rest)) = line.split_once(char::is_whitespace) else {
continue;
};
if hash.len() != 64 || !hash.chars().all(|c| c.is_ascii_hexdigit()) {
continue;
}
let name = rest.trim_start().trim_start_matches('*').trim();
let same =
name == file || Path::new(name).file_name().and_then(|n| n.to_str()) == Some(file);
if same {
return Some(hash.to_ascii_lowercase());
}
}
None
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn sums_accepts_text_and_binary_mode_lines() {
let text = "\
b546e4e4c085b5f3699220f8f1dee442bd65d389f0930e376f37ac5effa99df0 pmpx-x86_64-unknown-linux-gnu.tar.gz
814c3e352f5c0a651a23b03f3f379135b314b625ed4422b2d27c7081126eac7a *pmpx-x86_64-pc-windows-msvc.zip
";
assert_eq!(
parse_sha256sums(text, "pmpx-x86_64-unknown-linux-gnu.tar.gz").as_deref(),
Some("b546e4e4c085b5f3699220f8f1dee442bd65d389f0930e376f37ac5effa99df0")
);
assert_eq!(
parse_sha256sums(text, "pmpx-x86_64-pc-windows-msvc.zip").as_deref(),
Some("814c3e352f5c0a651a23b03f3f379135b314b625ed4422b2d27c7081126eac7a")
);
}
#[test]
fn sums_ignores_crlf_and_an_unknown_name() {
let text =
"b546e4e4c085b5f3699220f8f1dee442bd65d389f0930e376f37ac5effa99df0 other.tar.gz\r\n";
assert_eq!(parse_sha256sums(text, "pmpx-other.tar.gz"), None);
assert_eq!(
parse_sha256sums(text, "other.tar.gz").as_deref(),
Some("b546e4e4c085b5f3699220f8f1dee442bd65d389f0930e376f37ac5effa99df0")
);
}
#[test]
fn sums_rejects_a_line_that_is_not_a_hash() {
let text = "not-a-hash pmpx-x86_64-unknown-linux-gnu.tar.gz\n";
assert_eq!(
parse_sha256sums(text, "pmpx-x86_64-unknown-linux-gnu.tar.gz"),
None
);
}
#[test]
fn sums_keeps_looking_past_a_line_without_whitespace() {
let text = "\
binary
b546e4e4c085b5f3699220f8f1dee442bd65d389f0930e376f37ac5effa99df0 pmpx-x86_64-unknown-linux-gnu.tar.gz
";
assert_eq!(
parse_sha256sums(text, "pmpx-x86_64-unknown-linux-gnu.tar.gz").as_deref(),
Some("b546e4e4c085b5f3699220f8f1dee442bd65d389f0930e376f37ac5effa99df0")
);
}
#[test]
fn sums_of_only_stray_lines_finds_nothing() {
assert_eq!(parse_sha256sums("binary\nlines\n", "anything"), None);
}
#[test]
fn sha256_matches_the_published_vector() {
assert_eq!(
sha256_hex(b"abc"),
"ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"
);
}
}