find_identical_files/
lib.rs1mod args;
2mod enumerations;
3mod error;
4mod excel;
5mod separator;
6mod structures;
7mod traits;
8
9cfg_if::cfg_if! {
11 if #[cfg(feature = "walkdir")] {
12 mod with_walkdir;
13 pub use with_walkdir::get_all_files;
14 } else {
15 mod with_jwalk;
17 pub use with_jwalk::get_all_files;
18 }
19}
20
21pub use self::{
22 args::Arguments,
23 enumerations::algo::{Algorithm, PathBufExtension, SliceExtension},
24 enumerations::procedures::*,
25 error::*,
26 separator::get_thousands_separator,
27 structures::file_info::{FileExtension, FileInfo},
28 structures::group_info::{GroupExtension, GroupInfo},
29 structures::key_info::Key,
30 structures::path_info::PathInfo,
31 structures::total_info::TotalInfo,
32};
33pub use excel::write_xlsx;
34use serde::Serializer;
35use std::{
36 fmt::{self, Write as FmtWrite}, fs::{self, File},
38 io::{self, Write as IoWrite}, path::{Path, PathBuf},
40 process::Command,
41 str,
42};
43
44pub const CSV_FILENAME: &str = "fif.csv";
45pub const XLSX_FILENAME: &str = "fif.xlsx";
46
47pub fn open_file<P>(path: &P) -> FIFResult<File>
51where
52 P: AsRef<Path> + std::fmt::Debug,
53{
54 fs::OpenOptions::new()
55 .read(true)
56 .write(false) .create(false) .open(path.as_ref())
59 .map_err(|error| {
60 let path_buf = path.as_ref().to_path_buf();
61 match error.kind() {
62 io::ErrorKind::NotFound => FIFError::FileNotFound { path: path_buf },
63 io::ErrorKind::PermissionDenied => FIFError::PermissionDenied { path: path_buf },
64 _ => FIFError::FileOpenError {
65 path: path_buf,
66 io_error: error,
67 },
68 }
69 })
70}
71
72pub fn get_path(arguments: &Arguments) -> FIFResult<PathBuf> {
74 let path: PathBuf = match &arguments.input_dir {
75 Some(path) => path.to_owned(),
76 None => PathBuf::from("."),
77 };
78
79 if arguments.extended_path {
80 Ok(fs::canonicalize(path)?) } else {
82 Ok(path) }
84}
85
86pub fn my_print(buffer: &[u8]) -> FIFResult<()> {
92 let print_msg = str::from_utf8(buffer)?;
95
96 print!("{print_msg}");
98
99 io::stdout().flush()?;
101
102 Ok(())
103}
104
105pub fn clear_terminal_screen() {
107 let result = if cfg!(target_os = "windows") {
108 Command::new("cmd").args(["/c", "cls"]).spawn()
109 } else {
110 Command::new("tput").arg("reset").spawn()
112 };
113
114 if result.is_err() {
116 print!("{esc}c", esc = 27 as char);
117 }
118}
119fn write_integer_with_separator<W: FmtWrite>(
124 integer: usize,
125 separator: char,
126 writer: &mut W,
127) -> fmt::Result {
128 let s = integer.to_string();
129 let bytes = s.as_bytes();
130 let len = bytes.len();
131
132 for (i, &byte) in bytes.iter().enumerate() {
133 if i > 0 && (len - i).is_multiple_of(3) {
135 writer.write_char(separator)?;
136 }
137 writer.write_char(byte as char)?;
138 }
139 Ok(())
140}
141
142pub fn split_and_insert(integer: usize, separator: char) -> FIFResult<String> {
148 let s_val = integer.to_string();
149 let len = s_val.len();
150
151 if len <= 3 {
153 return Ok(s_val);
154 }
155
156 let num_seps = (len - 1) / 3;
159 let final_capacity = len + (num_seps * separator.len_utf8());
160
161 let mut result = String::with_capacity(final_capacity);
163
164 write_integer_with_separator(integer, separator, &mut result)?;
167
168 Ok(result)
169}
170
171struct BytesFormatter(usize);
174
175impl fmt::Display for BytesFormatter {
176 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
177 let sep = get_thousands_separator();
178
179 write_integer_with_separator(self.0, sep, f)?;
181
182 f.write_str(" bytes")
184 }
185}
186
187pub fn add_thousands_separator<S>(size: &usize, serializer: S) -> Result<S::Ok, S::Error>
191where
192 S: Serializer,
193{
194 serializer.collect_str(&BytesFormatter(*size))
196}
197
198#[cfg(test)]
199mod tests_lib {
200 use super::*;
201
202 #[test]
203 fn split_integer_into_groups() -> FIFResult<()> {
205 let mut result: Vec<String> = Vec::new();
206
207 for integer in [
208 0, 1, 12, 999, 1000, 1001, 1234, 12345, 123456, 1234567, 12345678,
209 ] {
210 let integer_splitted: String = split_and_insert(integer, '_')?;
211 println!("integer: {integer:<8} ; with thousands sep: {integer_splitted}");
212 result.push(integer_splitted);
213 }
214
215 let valid = vec![
216 "0",
217 "1",
218 "12",
219 "999",
220 "1_000",
221 "1_001",
222 "1_234",
223 "12_345",
224 "123_456",
225 "1_234_567",
226 "12_345_678",
227 ];
228
229 assert_eq!(valid, result);
230 Ok(())
231 }
232}