Skip to content

Commit e8174df

Browse files
committed
Better saving/loading.
Add support for loading/saving json files in release mode
1 parent e280a89 commit e8174df

11 files changed

+203
-159
lines changed

czkawka_core/src/common.rs

+29-9
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,16 @@ use std::time::SystemTime;
1212
1313
pub struct Common();
1414

15-
pub fn open_cache_folder(cache_file_name: &str, save_to_cache: bool, warnings: &mut Vec<String>) -> Option<(File, PathBuf)> {
15+
pub fn open_cache_folder(cache_file_name: &str, save_to_cache: bool, use_json: bool, warnings: &mut Vec<String>) -> Option<((Option<File>, PathBuf), (Option<File>, PathBuf))> {
1616
if let Some(proj_dirs) = ProjectDirs::from("pl", "Qarmin", "Czkawka") {
1717
let cache_dir = PathBuf::from(proj_dirs.cache_dir());
1818
let cache_file = cache_dir.join(cache_file_name);
19-
let file_handler = if save_to_cache {
19+
let cache_file_json = cache_dir.join(cache_file_name.replace(".bin", ".json"));
20+
21+
let mut file_handler_default = None;
22+
let mut file_handler_json = None;
23+
24+
if save_to_cache {
2025
if cache_dir.exists() {
2126
if !cache_dir.is_dir() {
2227
warnings.push(format!("Config dir {} is a file!", cache_dir.display()));
@@ -27,25 +32,40 @@ pub fn open_cache_folder(cache_file_name: &str, save_to_cache: bool, warnings: &
2732
return None;
2833
}
2934

30-
match OpenOptions::new().truncate(true).write(true).create(true).open(&cache_file) {
35+
file_handler_default = Some(match OpenOptions::new().truncate(true).write(true).create(true).open(&cache_file) {
3136
Ok(t) => t,
3237
Err(e) => {
3338
warnings.push(format!("Cannot create or open cache file {}, reason {}", cache_file.display(), e));
3439
return None;
3540
}
41+
});
42+
if use_json {
43+
file_handler_json = Some(match OpenOptions::new().truncate(true).write(true).create(true).open(&cache_file_json) {
44+
Ok(t) => t,
45+
Err(e) => {
46+
warnings.push(format!("Cannot create or open cache file {}, reason {}", cache_file_json.display(), e));
47+
return None;
48+
}
49+
});
3650
}
3751
} else {
38-
match OpenOptions::new().read(true).open(&cache_file) {
39-
Ok(t) => t,
40-
Err(_inspected) => {
41-
// messages.push(format!("Cannot find or open cache file {}", cache_file.display())); // No error warning
52+
if let Ok(t) = OpenOptions::new().read(true).open(&cache_file) {
53+
file_handler_default = Some(t);
54+
} else {
55+
if use_json {
56+
file_handler_json = Some(match OpenOptions::new().read(true).open(&cache_file_json) {
57+
Ok(t) => t,
58+
Err(_) => return None,
59+
});
60+
} else {
61+
// messages.push(format!("Cannot find or open cache file {}", cache_file.display())); // No error or warning
4262
return None;
4363
}
4464
}
4565
};
46-
return Some((file_handler, cache_file));
66+
return Some(((file_handler_default, cache_file), (file_handler_json, cache_file_json)));
4767
}
48-
return None;
68+
None
4969
}
5070

5171
pub fn get_dynamic_image_from_raw_image(path: impl AsRef<Path> + std::fmt::Debug) -> Option<DynamicImage> {

czkawka_core/src/duplicate.rs

+11-3
Original file line numberDiff line numberDiff line change
@@ -1261,8 +1261,9 @@ pub fn make_hard_link(src: &Path, dst: &Path) -> io::Result<()> {
12611261
}
12621262

12631263
pub fn save_hashes_to_file(hashmap: &BTreeMap<String, FileEntry>, text_messages: &mut Messages, type_of_hash: &HashType, is_prehash: bool, minimal_cache_file_size: u64) {
1264-
if let Some((file_handler, cache_file)) = open_cache_folder(&get_file_hash_name(type_of_hash, is_prehash), true, &mut text_messages.warnings) {
1265-
let mut writer = BufWriter::new(file_handler);
1264+
if let Some(((file_handler, cache_file), (_json_file, _json_name))) = open_cache_folder(&get_file_hash_name(type_of_hash, is_prehash), true, false, &mut text_messages.warnings)
1265+
{
1266+
let mut writer = BufWriter::new(file_handler.unwrap()); // Unwrap cannot fail
12661267

12671268
let mut how_much = 0;
12681269
for file_entry in hashmap.values() {
@@ -1285,7 +1286,14 @@ pub fn save_hashes_to_file(hashmap: &BTreeMap<String, FileEntry>, text_messages:
12851286
}
12861287
}
12871288
pub fn load_hashes_from_file(text_messages: &mut Messages, delete_outdated_cache: bool, type_of_hash: &HashType, is_prehash: bool) -> Option<BTreeMap<u64, Vec<FileEntry>>> {
1288-
if let Some((file_handler, cache_file)) = open_cache_folder(&get_file_hash_name(type_of_hash, is_prehash), false, &mut text_messages.warnings) {
1289+
if let Some(((file_handler, cache_file), (_json_file, _json_name))) =
1290+
open_cache_folder(&get_file_hash_name(type_of_hash, is_prehash), false, false, &mut text_messages.warnings)
1291+
{
1292+
// Unwrap could fail when failed to open cache file, but json would exists
1293+
let file_handler = match file_handler {
1294+
Some(t) => t,
1295+
_ => return Default::default(),
1296+
};
12891297
let reader = BufReader::new(file_handler);
12901298

12911299
let mut hashmap_loaded_entries: BTreeMap<u64, Vec<FileEntry>> = Default::default();

czkawka_core/src/lib.rs

+3
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
#![allow(clippy::collapsible_else_if)]
2+
#![allow(clippy::type_complexity)]
3+
14
#[macro_use]
25
extern crate bitflags;
36

czkawka_core/src/similar_images.rs

+67-94
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,4 @@
1-
<<<<<<< HEAD
21
use std::collections::{BTreeSet, HashMap, HashSet};
3-
use std::fs::OpenOptions;
4-
=======
5-
use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
6-
>>>>>>> 42e62c3 (Simplify cache code)
72
use std::fs::{File, Metadata};
83
use std::io::Write;
94
use std::io::*;
@@ -133,6 +128,7 @@ pub struct SimilarImages {
133128
exclude_images_with_same_size: bool,
134129
use_reference_folders: bool,
135130
fast_comparing: bool,
131+
save_also_as_json: bool,
136132
}
137133

138134
/// Info struck with helpful information's about results
@@ -176,6 +172,7 @@ impl SimilarImages {
176172
exclude_images_with_same_size: false,
177173
use_reference_folders: false,
178174
fast_comparing: false,
175+
save_also_as_json: false,
179176
}
180177
}
181178

@@ -207,6 +204,9 @@ impl SimilarImages {
207204
pub fn set_fast_comparing(&mut self, fast_comparing: bool) {
208205
self.fast_comparing = fast_comparing;
209206
}
207+
pub fn set_save_also_as_json(&mut self, save_also_as_json: bool) {
208+
self.save_also_as_json = save_also_as_json;
209+
}
210210

211211
pub fn get_stopped_search(&self) -> bool {
212212
self.stopped_search
@@ -647,7 +647,14 @@ impl SimilarImages {
647647
for (file_entry, _hash) in vec_file_entry {
648648
all_results.insert(file_entry.path.to_string_lossy().to_string(), file_entry);
649649
}
650-
save_hashes_to_file(&all_results, &mut self.text_messages, self.hash_size, self.hash_alg, self.image_filter);
650+
save_hashes_to_file(
651+
&all_results,
652+
&mut self.text_messages,
653+
self.save_also_as_json,
654+
self.hash_size,
655+
self.hash_alg,
656+
self.image_filter,
657+
);
651658
}
652659

653660
Common::print_time(hash_map_modification, SystemTime::now(), "sort_images - saving data to files".to_string());
@@ -1036,49 +1043,36 @@ impl PrintResults for SimilarImages {
10361043
}
10371044
}
10381045

1039-
<<<<<<< HEAD
1040-
pub fn save_hashes_to_file(hashmap: &HashMap<String, FileEntry>, text_messages: &mut Messages, hash_size: u8, hash_alg: HashAlg, image_filter: FilterType) {
1041-
if let Some(proj_dirs) = ProjectDirs::from("pl", "Qarmin", "Czkawka") {
1042-
let cache_dir = PathBuf::from(proj_dirs.cache_dir());
1043-
if cache_dir.exists() {
1044-
if !cache_dir.is_dir() {
1045-
text_messages.messages.push(format!("Config dir {} is a file!", cache_dir.display()));
1046-
return;
1047-
}
1048-
} else if let Err(e) = fs::create_dir_all(&cache_dir) {
1049-
text_messages.messages.push(format!("Cannot create config dir {}, reason {}", cache_dir.display(), e));
1050-
return;
1051-
}
1052-
1053-
let cache_file = cache_dir.join(cache_dir.join(get_cache_file(&hash_size, &hash_alg, &image_filter)));
1054-
let file_handler = match OpenOptions::new().truncate(true).write(true).create(true).open(&cache_file) {
1055-
Ok(t) => t,
1056-
Err(e) => {
1046+
pub fn save_hashes_to_file(
1047+
hashmap: &HashMap<String, FileEntry>,
1048+
text_messages: &mut Messages,
1049+
save_also_as_json: bool,
1050+
hash_size: u8,
1051+
hash_alg: HashAlg,
1052+
image_filter: FilterType,
1053+
) {
1054+
if let Some(((file_handler, cache_file), (file_handler_json, cache_file_json))) =
1055+
open_cache_folder(&get_cache_file(&hash_size, &hash_alg, &image_filter), true, save_also_as_json, &mut text_messages.warnings)
1056+
{
1057+
{
1058+
let writer = BufWriter::new(file_handler.unwrap()); // Unwrap because cannot fail here
1059+
if let Err(e) = bincode::serialize_into(writer, hashmap) {
10571060
text_messages
1058-
.messages
1059-
.push(format!("Cannot create or open cache file {}, reason {}", cache_file.display(), e));
1061+
.warnings
1062+
.push(format!("Cannot write data to cache file {}, reason {}", cache_file.display(), e));
10601063
return;
10611064
}
1062-
};
1063-
1064-
=======
1065-
pub fn save_hashes_to_file(hashmap: &BTreeMap<String, FileEntry>, text_messages: &mut Messages, hash_size: u8, hash_alg: HashAlg, image_filter: FilterType) {
1066-
if let Some((file_handler, cache_file)) = open_cache_folder(&get_cache_file(&hash_size, &hash_alg, &image_filter), true, &mut text_messages.warnings) {
1067-
>>>>>>> 42e62c3 (Simplify cache code)
1068-
let writer = BufWriter::new(file_handler);
1069-
#[cfg(not(debug_assertions))]
1070-
if let Err(e) = bincode::serialize_into(writer, hashmap) {
1071-
text_messages
1072-
.warnings
1073-
.push(format!("Cannot write data to cache file {}, reason {}", cache_file.display(), e));
1074-
return;
10751065
}
1076-
#[cfg(debug_assertions)]
1077-
if let Err(e) = serde_json::to_writer(writer, hashmap) {
1078-
text_messages
1079-
.warnings
1080-
.push(format!("Cannot write data to cache file {}, reason {}", cache_file.display(), e));
1081-
return;
1066+
if save_also_as_json {
1067+
if let Some(file_handler_json) = file_handler_json {
1068+
let writer = BufWriter::new(file_handler_json);
1069+
if let Err(e) = serde_json::to_writer(writer, hashmap) {
1070+
text_messages
1071+
.warnings
1072+
.push(format!("Cannot write data to cache file {}, reason {}", cache_file_json.display(), e));
1073+
return;
1074+
}
1075+
}
10821076
}
10831077

10841078
text_messages.messages.push(format!("Properly saved to file {} cache entries.", hashmap.len()));
@@ -1091,44 +1085,34 @@ pub fn load_hashes_from_file(
10911085
hash_size: u8,
10921086
hash_alg: HashAlg,
10931087
image_filter: FilterType,
1094-
<<<<<<< HEAD
10951088
) -> Option<HashMap<String, FileEntry>> {
1096-
if let Some(proj_dirs) = ProjectDirs::from("pl", "Qarmin", "Czkawka") {
1097-
let cache_dir = PathBuf::from(proj_dirs.cache_dir());
1098-
let cache_file = cache_dir.join(get_cache_file(&hash_size, &hash_alg, &image_filter));
1099-
let file_handler = match OpenOptions::new().read(true).open(&cache_file) {
1100-
Ok(t) => t,
1101-
Err(_inspected) => {
1102-
// text_messages.messages.push(format!("Cannot find or open cache file {}", cache_file.display())); // No error warning
1103-
return None;
1104-
}
1105-
};
1106-
1107-
=======
1108-
) -> Option<BTreeMap<String, FileEntry>> {
1109-
if let Some((file_handler, cache_file)) = open_cache_folder(&get_cache_file(&hash_size, &hash_alg, &image_filter), false, &mut text_messages.warnings) {
1110-
>>>>>>> 42e62c3 (Simplify cache code)
1111-
let reader = BufReader::new(file_handler);
1112-
#[cfg(debug_assertions)]
1113-
let mut hashmap_loaded_entries: HashMap<String, FileEntry> = match serde_json::from_reader(reader) {
1114-
Ok(t) => t,
1115-
Err(e) => {
1116-
text_messages
1117-
.warnings
1118-
.push(format!("Failed to load data from cache file {}, reason {}", cache_file.display(), e));
1119-
return None;
1120-
}
1121-
};
1122-
#[cfg(not(debug_assertions))]
1123-
let mut hashmap_loaded_entries: HashMap<String, FileEntry> = match bincode::deserialize_from(reader) {
1124-
Ok(t) => t,
1125-
Err(e) => {
1126-
text_messages
1127-
.warnings
1128-
.push(format!("Failed to load data from cache file {}, reason {}", cache_file.display(), e));
1129-
return None;
1130-
}
1131-
};
1089+
if let Some(((file_handler, cache_file), (file_handler_json, cache_file_json))) =
1090+
open_cache_folder(&get_cache_file(&hash_size, &hash_alg, &image_filter), false, true, &mut text_messages.warnings)
1091+
{
1092+
let mut hashmap_loaded_entries: HashMap<String, FileEntry>;
1093+
if let Some(file_handler) = file_handler {
1094+
let reader = BufReader::new(file_handler);
1095+
hashmap_loaded_entries = match bincode::deserialize_from(reader) {
1096+
Ok(t) => t,
1097+
Err(e) => {
1098+
text_messages
1099+
.warnings
1100+
.push(format!("Failed to load data from cache file {}, reason {}", cache_file.display(), e));
1101+
return None;
1102+
}
1103+
};
1104+
} else {
1105+
let reader = BufReader::new(file_handler_json.unwrap()); // Unwrap cannot fail, because at least one file must be valid
1106+
hashmap_loaded_entries = match serde_json::from_reader(reader) {
1107+
Ok(t) => t,
1108+
Err(e) => {
1109+
text_messages
1110+
.warnings
1111+
.push(format!("Failed to load data from cache file {}, reason {}", cache_file_json.display(), e));
1112+
return None;
1113+
}
1114+
};
1115+
}
11321116

11331117
// Don't load cache data if destination file not exists
11341118
if delete_outdated_cache {
@@ -1143,22 +1127,11 @@ pub fn load_hashes_from_file(
11431127
}
11441128

11451129
fn get_cache_file(hash_size: &u8, hash_alg: &HashAlg, image_filter: &FilterType) -> String {
1146-
let extension;
1147-
#[cfg(debug_assertions)]
1148-
{
1149-
extension = "json";
1150-
}
1151-
#[cfg(not(debug_assertions))]
1152-
{
1153-
extension = "bin";
1154-
}
1155-
11561130
format!(
1157-
"cache_similar_images_{}_{}_{}.{}",
1131+
"cache_similar_images_{}_{}_{}.bin",
11581132
hash_size,
11591133
convert_algorithm_to_string(hash_alg),
11601134
convert_filters_to_string(image_filter),
1161-
extension
11621135
)
11631136
}
11641137

0 commit comments

Comments
 (0)