1mod config;
2mod dependencies;
3mod desktops;
4mod dimension;
5mod environment;
6mod error;
7mod fileinfo;
8mod monitors;
9mod orientation;
10mod pids;
11mod traits;
12mod walkdir;
13
14pub use self::{
15 config::*, dependencies::*, desktops::*, dimension::*, environment::*, error::*, fileinfo::*,
16 monitors::*, orientation::*, pids::*, traits::*, walkdir::*,
17};
18
19cfg_if::cfg_if! {
21 if #[cfg(feature = "args_v2")] {
22 mod args_v2;
23 pub use args_v2::*;
24 } else {
25 mod args_v1;
27 pub use args_v1::*;
28 }
29}
30
31use std::{
33 collections::HashMap,
34 env,
35 hash::{BuildHasher, Hasher, RandomState},
36 path::PathBuf,
37 thread,
38};
39
40pub fn show_initial_msgs(config: &Config) -> WallSwitchResult<()> {
42 let pkg_name = ENVIRON.get_pkg_name();
43 let pkg_desc = env!("CARGO_PKG_DESCRIPTION");
44 let pkg_version = env!("CARGO_PKG_VERSION");
45 let interval = config.interval;
46 let info = format!("Interval between each wallpaper: {interval} seconds.");
47 let author = "Claudio Fernandes de Souza Rodrigues (claudiofsrodrigues@gmail.com)";
48
49 println!("{pkg_name} {pkg_desc}\n{info}\n{author}");
50 println!("version: {pkg_version}\n");
51
52 let depend1 = "imagemagick (image viewing/manipulation program)";
53 let depend2 = "feh (fast and light image viewer)";
54 let dependencies = [depend1, depend2];
55
56 println!("Dependencies:");
57 dependencies.print_with_spaces(" ");
58 println!();
59
60 config.print()?;
61
62 Ok(())
63}
64
65pub fn get_images(config: &Config) -> WallSwitchResult<Vec<FileInfo>> {
67 let mut images: Vec<FileInfo> = gather_files(config)?;
68
69 if images.is_empty() {
70 let directories = config.directories.clone();
71 let error = WallSwitchError::NoImages { paths: directories };
72 eprintln!("{error}");
73 std::process::exit(1);
75 }
76
77 let nimages: usize = images.len();
78
79 if nimages < config.monitors.len() {
80 let directories: Vec<PathBuf> = images.iter().map(|f| f.path.clone()).collect();
81 let error = WallSwitchError::InsufficientImages {
82 paths: directories,
83 nfiles: nimages,
84 };
85 eprintln!("{error}");
86 std::process::exit(1);
88 }
89
90 images.update_number();
91
92 if !config.sort {
93 shuffle(&mut images);
94 }
95
96 Ok(images)
97}
98
99fn gather_files(config: &Config) -> WallSwitchResult<Vec<FileInfo>> {
103 let mut files: Vec<FileInfo> = Vec::new();
104 let mut group_by: HashMap<String, Vec<PathBuf>> = HashMap::new();
105
106 for dir in &config.directories {
107 let mut infos: Vec<FileInfo> = get_files_from_directory(dir, config)?;
109 infos.update_hash()?;
110
111 for info in infos {
112 let hash: String = info.hash.clone();
113 let path: PathBuf = info.path.clone();
114
115 if !group_by.contains_key(&hash) {
117 files.push(info);
118 }
119
120 group_by.entry(hash).or_default().push(path);
121 }
122 }
123
124 if config.verbose {
126 group_by
127 .values()
128 .filter(|paths| paths.len() > 1)
129 .for_each(|paths| {
130 println!("{id}: {paths:#?}\n", id = "identical files".yellow());
131 });
132 }
133
134 Ok(files)
135}
136
137pub fn shuffle<T>(vec: &mut [T]) {
162 let n: usize = vec.len();
163 for i in 0..(n - 1) {
164 let j = (rand() as usize) % (n - i) + i;
167 vec.swap(i, j);
168 }
169}
170
171pub fn get_random_integer(min: u64, max: u64) -> u64 {
173 min + rand() % (max - min + 1)
174}
175
176pub fn get_random_integer_v2(min: u64, max: u64) -> WallSwitchResult<u64> {
180 if min > max {
181 Err(WallSwitchError::MinMax { min, max })
182 } else {
183 Ok(min + rand() % (max - min + 1))
185 }
186}
187
188pub fn rand() -> u64 {
190 RandomState::new().build_hasher().finish()
191}
192
193pub fn display_files(files: &[FileInfo], config: &Config) {
195 let nfiles = files.len();
196 let ndigits = nfiles.count_chars();
197
198 if config.sort {
199 println!("{nfiles} images were found (sorted):");
200 } else {
201 println!("{nfiles} images were found (shuffled):");
202 }
203
204 for file in files.iter() {
205 println!(
206 "images[{n:0ndigits$}/{t}]: {p:?}",
207 n = file.number,
208 p = file.path,
209 t = file.total,
210 );
211 }
212 println!();
213}
214
215pub fn update_images(files: &[FileInfo], config: &Config) -> Vec<FileInfo> {
223 let mut owned_files: Vec<FileInfo> = files.to_vec();
224
225 thread::scope(|scope| {
226 for file in &mut owned_files {
227 scope.spawn(move || -> WallSwitchResult<()> {
228 file.update_info(config)
231 });
232 }
233 });
234
235 owned_files
236}
237
238#[cfg(test)]
283mod test_lib {
284 use crate::*;
285
286 #[test]
287 fn vec_shuffle() {
289 let mut vec: Vec<u32> = (1..=100).collect();
290 shuffle(&mut vec);
291
292 println!("vec: {vec:?}");
293 assert_eq!(vec.len(), 100);
294 }
295
296 #[test]
297 fn random_integers_v1() {
301 let value: u64 = get_random_integer(1, 20);
303
304 println!("integer: {value:?}");
305
306 let integers: Vec<u64> = (0..100).map(|_| get_random_integer(1, 20)).collect();
310
311 println!("integers: {integers:?}");
312
313 let condition_a = integers.iter().min() >= Some(&1);
314 let condition_b = integers.iter().max() <= Some(&20);
315
316 assert!(condition_a);
317 assert!(condition_b);
318 assert_eq!(integers.len(), 100);
319 }
320
321 #[test]
322 fn random_integers_v2() -> WallSwitchResult<()> {
326 let value: u64 = get_random_integer_v2(1, 20)?;
328
329 println!("integer: {value:?}");
330
331 let integers: Vec<u64> = (0..100)
335 .map(|_| get_random_integer_v2(1, 20))
336 .collect::<Result<Vec<u64>, _>>()?;
337
338 println!("integers: {integers:?}");
339
340 let condition_a = integers.iter().min() >= Some(&1);
341 let condition_b = integers.iter().max() <= Some(&20);
342
343 assert!(condition_a);
344 assert!(condition_b);
345 assert_eq!(integers.len(), 100);
346
347 Ok(())
348 }
349
350 #[test]
351 fn random_integers_v3() -> WallSwitchResult<()> {
353 let result = get_random_integer_v2(21, 20).map_err(|err| {
354 eprintln!("{err}");
355 err
356 });
357 assert!(result.is_err());
358
359 let error = result.unwrap_err();
360 eprintln!("error: {error:?}");
361
362 Ok(())
363 }
364}