Skip to main content

builder_cpp/
hasher.rs

1//! This module contains functions for hashing files and checking if they have changed.
2use crate::utils::log::{log, LogLevel};
3use sha1::{Digest, Sha1};
4use std::collections::HashMap;
5use std::fs::{File, OpenOptions};
6use std::io::{Read, Write};
7use std::path::Path;
8
9// Hashes a file and returns the hash as a string.
10fn hash_file(path: &str) -> String {
11    let mut file = File::open(path).unwrap();
12    const CHUNK_SIZE: usize = 1024 * 1024;
13
14    let mut limit = file
15        .metadata()
16        .unwrap_or_else(|why| {
17            log(
18                LogLevel::Error,
19                &format!("Failed to get length for file: {}", path),
20            );
21            log(LogLevel::Error, &format!("Error: {}", why));
22            std::process::exit(1);
23        })
24        .len();
25    let mut buffer = [0; CHUNK_SIZE];
26    let mut hasher = Sha1::new();
27
28    while limit > 0 {
29        let read_size = if limit < CHUNK_SIZE as u64 {
30            limit as usize
31        } else {
32            CHUNK_SIZE
33        };
34        let read = file.read(&mut buffer[0..read_size]).unwrap();
35        if read == 0 {
36            break;
37        }
38        limit -= read as u64;
39        hasher.update(&buffer[0..read]);
40    }
41    let result = hasher.finalize();
42    let mut hash = String::new();
43    for byte in result {
44        hash.push_str(&format!("{:02x}", byte));
45    }
46    hash
47}
48
49/// Returns the hash of a file if it exists in the path_hash.
50/// Otherwise returns None.
51/// # Arguments
52/// * `path` - The path of the file to get the hash of.
53/// * `path_hash` - The hashmap of paths and hashes.
54pub fn get_hash(path: &str, path_hash: &HashMap<String, String>) -> Option<String> {
55    if path_hash.contains_key(path) {
56        Some(path_hash.get(path).unwrap().to_string())
57    } else {
58        None
59    }
60}
61
62/// Loads the hashes from a file and returns them as a hashmap.
63/// # Arguments
64/// * `path` - The path of the file to load the hashes from.
65pub fn load_hashes_from_file(path: &str) -> HashMap<String, String> {
66    let mut path_hash: HashMap<String, String> = HashMap::new();
67    let path = Path::new(path);
68    if !path.exists() {
69        return path_hash;
70    }
71    let mut file = OpenOptions::new().read(true).open(path).unwrap();
72    let mut contents = String::new();
73    file.read_to_string(&mut contents).unwrap();
74    for line in contents.lines() {
75        if line.is_empty() {
76            continue;
77        }
78        let mut split = line.split(' ');
79        let path = split.next().unwrap();
80        let hash = split.next().unwrap();
81        path_hash.insert(path.to_string(), hash.to_string());
82    }
83    path_hash
84}
85
86/// Saves the hashes to a file.
87/// # Arguments
88/// * `path` - The path of the file to save the hashes to.
89/// * `path_hash` - The hashmap of paths and hashes.
90pub fn save_hashes_to_file(path: &str, path_hash: &HashMap<String, String>) {
91    let mut file = OpenOptions::new()
92        .write(true)
93        .create(true)
94        .open(path)
95        .unwrap_or_else(|_| {
96            log(LogLevel::Error, &format!("Failed to open file: {}", path));
97            std::process::exit(1);
98        });
99    for (path, hash) in path_hash {
100        let line = format!("{} {}\n", path, hash);
101        file.write_all(line.as_bytes()).unwrap();
102    }
103}
104
105/// Checks if a file has changed.
106/// # Arguments
107/// * `path` - The path of the file to check.
108/// * `path_hash` - The hashmap of paths and hashes.
109pub fn is_file_changed(path: &str, path_hash: &HashMap<String, String>) -> bool {
110    let hash = get_hash(path, path_hash);
111    if hash.is_none() {
112        return true;
113    }
114    let hash = hash.unwrap();
115    let new_hash = hash_file(path);
116    hash != new_hash
117}
118
119/// Saves the hash of a file to the hashmap.
120/// # Arguments
121/// * `path` - The path of the file to save the hash of.
122/// * `path_hash` - The hashmap of paths and hashes.
123pub fn save_hash(path: &str, path_hash: &mut HashMap<String, String>) {
124    let new_hash = hash_file(path);
125    let hash = get_hash(path, path_hash);
126    if hash.is_none() {
127        path_hash.insert(path.to_string(), new_hash);
128        return;
129    }
130    let hash = hash.unwrap();
131    if hash != new_hash {
132        log(
133            LogLevel::Info,
134            &format!("File changed, updating hash for file: {}", path),
135        );
136        path_hash.insert(path.to_string(), new_hash);
137    }
138}