1use 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
9fn 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
49pub 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
62pub 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
86pub 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
105pub 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
119pub 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}