1 use crate::manifest
::{FileHash, Manifest}
;
3 use sha2
::{Digest, Sha256}
;
4 use std
::collections
::{HashMap, HashSet}
;
7 use std
::io
::BufReader
;
8 use std
::path
::{Path, PathBuf}
;
10 use std
::time
::Instant
;
12 pub(crate) struct Checksums
{
13 cache_path
: Option
<PathBuf
>,
14 collected
: Mutex
<HashMap
<PathBuf
, String
>>,
18 pub(crate) fn new() -> Result
<Self, Box
<dyn Error
>> {
19 let cache_path
= std
::env
::var_os("BUILD_MANIFEST_CHECKSUM_CACHE").map(PathBuf
::from
);
21 let mut collected
= HashMap
::new();
22 if let Some(path
) = &cache_path
{
24 collected
= serde_json
::from_slice(&std
::fs
::read(path
)?
)?
;
28 Ok(Checksums { cache_path, collected: Mutex::new(collected) }
)
31 pub(crate) fn store_cache(&self) -> Result
<(), Box
<dyn Error
>> {
32 if let Some(path
) = &self.cache_path
{
33 std
::fs
::write(path
, &serde_json
::to_vec(&self.collected
)?
)?
;
38 pub(crate) fn fill_missing_checksums(&mut self, manifest
: &mut Manifest
) {
39 let need_checksums
= self.find_missing_checksums(manifest
);
40 if !need_checksums
.is_empty() {
41 self.collect_checksums(&need_checksums
);
43 self.replace_checksums(manifest
);
46 fn find_missing_checksums(&mut self, manifest
: &mut Manifest
) -> HashSet
<PathBuf
> {
47 let collected
= self.collected
.lock().unwrap();
48 let mut need_checksums
= HashSet
::new();
49 crate::manifest
::visit_file_hashes(manifest
, |file_hash
| {
50 if let FileHash
::Missing(path
) = file_hash
{
51 let path
= std
::fs
::canonicalize(path
).unwrap();
52 if !collected
.contains_key(&path
) {
53 need_checksums
.insert(path
);
60 fn replace_checksums(&mut self, manifest
: &mut Manifest
) {
61 let collected
= self.collected
.lock().unwrap();
62 crate::manifest
::visit_file_hashes(manifest
, |file_hash
| {
63 if let FileHash
::Missing(path
) = file_hash
{
64 let path
= std
::fs
::canonicalize(path
).unwrap();
65 match collected
.get(&path
) {
66 Some(hash
) => *file_hash
= FileHash
::Present(hash
.clone()),
67 None
=> panic
!("missing hash for file {}", path
.display()),
73 fn collect_checksums(&mut self, files
: &HashSet
<PathBuf
>) {
74 let collection_start
= Instant
::now();
76 "collecting hashes for {} tarballs across {} threads",
78 rayon
::current_num_threads().min(files
.len()),
81 files
.par_iter().for_each(|path
| match hash(path
) {
83 self.collected
.lock().unwrap().insert(path
.clone(), hash
);
85 Err(err
) => eprintln
!("error while fetching the hash for {}: {}", path
.display(), err
),
88 println
!("collected {} hashes in {:.2?}", files
.len(), collection_start
.elapsed());
92 fn hash(path
: &Path
) -> Result
<String
, Box
<dyn Error
>> {
93 let mut file
= BufReader
::new(File
::open(path
)?
);
94 let mut sha256
= Sha256
::default();
95 std
::io
::copy(&mut file
, &mut sha256
)?
;
96 Ok(hex
::encode(sha256
.finalize()))