SPEC.md and ARCHITECTURE.md were sitting at the repo root alongside Cargo.toml/Makefile.toml/packages.d — moved both into docs/ (doubleo7 already does this for its own supplementary docs, so this matches an existing convention in the fleet rather than inventing a new one). Updated every doc-comment cross-reference across src/*.rs and Makefile.toml (23 references) to the new docs/SPEC.md / docs/ ARCHITECTURE.md paths. The two files' own cross-references to each other didn't need changing — they're still same-directory relative references. Also updated the project reference memory pointing at ARCHITECTURE.md's location, so it doesn't go stale pointing at a path that no longer exists. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
81 lines
2.7 KiB
Rust
81 lines
2.7 KiB
Rust
//! Two functions, shared by two real callers (`verifier`, `builder`) —
|
|
//! not a general-purpose utils dump. See docs/ARCHITECTURE.md > "organize by
|
|
//! pipeline stage, not by layer" for why that distinction matters.
|
|
|
|
use anyhow::{Context, Result};
|
|
use sha2::{Digest, Sha256};
|
|
use std::io::Read;
|
|
use std::path::Path;
|
|
|
|
/// In-memory digest — test-only now that both real callers (`verifier`,
|
|
/// `builder`) hash a file already on disk via `sha256_hex_file` instead.
|
|
/// Kept for building expected hashes from in-memory test fixtures.
|
|
#[cfg(test)]
|
|
pub(crate) fn sha256_hex(data: &[u8]) -> String {
|
|
let mut hasher = Sha256::new();
|
|
hasher.update(data);
|
|
hex::encode(hasher.finalize())
|
|
}
|
|
|
|
/// Same digest as `sha256_hex(&std::fs::read(path)?)`, but streamed in
|
|
/// fixed-size chunks instead of reading the whole file into memory first —
|
|
/// downloaded release assets are tens of MB, and the file is already on
|
|
/// disk, so there's no reason to hold a second full copy in memory just to
|
|
/// hash it.
|
|
pub fn sha256_hex_file(path: &Path) -> Result<String> {
|
|
let mut file =
|
|
std::fs::File::open(path).with_context(|| format!("opening {}", path.display()))?;
|
|
let mut hasher = Sha256::new();
|
|
let mut buf = [0u8; 64 * 1024];
|
|
loop {
|
|
let n = file
|
|
.read(&mut buf)
|
|
.with_context(|| format!("reading {}", path.display()))?;
|
|
if n == 0 {
|
|
break;
|
|
}
|
|
hasher.update(&buf[..n]);
|
|
}
|
|
Ok(hex::encode(hasher.finalize()))
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn matches_known_sha256() {
|
|
// printf 'hello world' | sha256sum
|
|
assert_eq!(
|
|
sha256_hex(b"hello world"),
|
|
"b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn sha256_hex_file_matches_in_memory_digest() {
|
|
let dir = tempfile::tempdir().unwrap();
|
|
let path = dir.path().join("data.bin");
|
|
// Bigger than one read chunk, to actually exercise the loop.
|
|
let data = vec![0x5au8; 200 * 1024];
|
|
std::fs::write(&path, &data).unwrap();
|
|
|
|
assert_eq!(sha256_hex_file(&path).unwrap(), sha256_hex(&data));
|
|
}
|
|
|
|
#[test]
|
|
fn sha256_hex_file_matches_for_empty_file() {
|
|
let dir = tempfile::tempdir().unwrap();
|
|
let path = dir.path().join("empty.bin");
|
|
std::fs::write(&path, b"").unwrap();
|
|
|
|
assert_eq!(sha256_hex_file(&path).unwrap(), sha256_hex(b""));
|
|
}
|
|
|
|
#[test]
|
|
fn sha256_hex_file_errors_on_missing_file() {
|
|
let dir = tempfile::tempdir().unwrap();
|
|
let missing = dir.path().join("does-not-exist.bin");
|
|
assert!(sha256_hex_file(&missing).is_err());
|
|
}
|
|
}
|