use std::fs::{File, OpenOptions};
use std::io::{Read, Write};
use std::path::Path;
use crate::utils::{log, LogLevel};
use std::collections::HashMap;
use sha1::{Sha1, Digest};
fn hash_file(path: &str) -> String {
let mut file = File::open(path).unwrap();
const CHUNK_SIZE: usize = 1024 * 1024;
let mut limit = file.metadata().unwrap_or_else(|why| {
log(LogLevel::Error, &format!("Failed to get length for file: {}", path));
log(LogLevel::Error, &format!("Error: {}", why));
std::process::exit(1);
}).len();
let mut buffer = [0; CHUNK_SIZE];
let mut hasher = Sha1::new();
while limit > 0 {
let read_size = if limit < CHUNK_SIZE as u64 {
limit as usize
} else {
CHUNK_SIZE
};
let read = file.read(&mut buffer[0..read_size]).unwrap();
if read == 0 {
break;
}
limit -= read as u64;
hasher.update(&buffer[0..read]);
}
let result = hasher.finalize();
let mut hash = String::new();
for byte in result {
hash.push_str(&format!("{:02x}", byte));
}
return hash;
}
pub fn get_hash(path: &str, path_hash: &HashMap<String, String>) -> Option<String> {
if path_hash.contains_key(path) {
return Some(path_hash.get(path).unwrap().to_string());
}
return None;
}
pub fn load_hashes_from_file(path: &str) -> HashMap<String, String> {
let mut path_hash: HashMap<String, String> = HashMap::new();
let path = Path::new(path);
if !path.exists() {
return path_hash;
}
let mut file = OpenOptions::new().read(true).open(path).unwrap();
let mut contents = String::new();
file.read_to_string(&mut contents).unwrap();
for line in contents.lines() {
if line.is_empty() {
continue;
}
let mut split = line.split(" ");
let path = split.next().unwrap();
let hash = split.next().unwrap();
path_hash.insert(path.to_string(), hash.to_string());
}
return path_hash;
}
pub fn save_hashes_to_file(path: &str, path_hash: &HashMap<String, String>) {
let mut file = OpenOptions::new().write(true).create(true).open(path).unwrap_or_else(|_| {
log(LogLevel::Error, &format!("Failed to open file: {}", path));
std::process::exit(1);
});
for (path, hash) in path_hash {
let line = format!("{} {}\n", path, hash);
file.write(line.as_bytes()).unwrap();
}
}
pub fn is_file_changed(path: &str, path_hash: &HashMap<String, String>) -> bool {
let hash = get_hash(path, path_hash);
if hash.is_none() {
return true;
}
let hash = hash.unwrap();
let new_hash = hash_file(path);
let result = hash != new_hash;
result
}
pub fn save_hash(path: &str, path_hash: &mut HashMap<String, String>) {
let new_hash = hash_file(path);
let hash = get_hash(path, path_hash);
if hash.is_none() {
path_hash.insert(path.to_string(), new_hash);
return;
}
let hash = hash.unwrap();
if hash != new_hash {
log(LogLevel::Info, &format!("File changed, updating hash for file: {}", path));
path_hash.insert(path.to_string(), new_hash);
return;
}
}