mod backends;
mod config;
mod dependencies;
mod desktop;
mod dimension;
mod environment;
mod error;
mod fileinfo;
mod monitors;
mod orientation;
mod pids;
mod traits;
mod walkdir;
pub use self::{
backends::*, config::*, dependencies::*, desktop::*, dimension::*, environment::*, error::*,
fileinfo::*, monitors::*, orientation::*, pids::*, traits::*, walkdir::*,
};
cfg_if::cfg_if! {
if #[cfg(feature = "args_v2")] {
mod args_v2;
pub use args_v2::*;
} else {
mod args_v1;
pub use args_v1::*;
}
}
use std::{
collections::HashMap,
env,
hash::{BuildHasher, Hasher, RandomState},
path::PathBuf,
thread,
};
pub fn show_initial_msgs(config: &Config) -> WallSwitchResult<()> {
let pkg_name = ENVIRON.get_pkg_name();
let pkg_desc = env!("CARGO_PKG_DESCRIPTION");
let pkg_version = env!("CARGO_PKG_VERSION");
let interval = config.interval;
let info = format!("Interval between each wallpaper: {interval} seconds.");
let author = "Claudio Fernandes de Souza Rodrigues (claudiofsrodrigues@gmail.com)";
println!("{pkg_name} {pkg_desc}\n{info}\n{author}");
println!("version: {pkg_version}\n");
let depend1 = "imagemagick (image viewing/manipulation program)";
let depend2 = "feh (fast and light image viewer)";
let dependencies = [depend1, depend2];
println!("Dependencies:");
dependencies.print_with_spaces(" ");
println!();
config.print()?;
Ok(())
}
pub fn get_images(config: &Config) -> WallSwitchResult<Vec<FileInfo>> {
let mut images: Vec<FileInfo> = gather_files(config)?;
if images.is_empty() {
let directories = config.directories.clone();
let error = WallSwitchError::NoImages { paths: directories };
eprintln!("{error}");
std::process::exit(1);
}
let nimages: usize = images.len();
if nimages < config.monitors.len() {
let directories: Vec<PathBuf> = images.iter().map(|f| f.path.clone()).collect();
let error = WallSwitchError::InsufficientImages {
paths: directories,
nfiles: nimages,
};
eprintln!("{error}");
std::process::exit(1);
}
images.update_number();
if !config.sort {
shuffle(&mut images);
}
Ok(images)
}
fn gather_files(config: &Config) -> WallSwitchResult<Vec<FileInfo>> {
let mut files: Vec<FileInfo> = Vec::new();
let mut group_by: HashMap<String, Vec<PathBuf>> = HashMap::new();
for dir in &config.directories {
let mut infos: Vec<FileInfo> = get_files_from_directory(dir, config)?;
infos.update_hash()?;
for info in infos {
let hash: String = info.hash.clone();
let path: PathBuf = info.path.clone();
if !group_by.contains_key(&hash) {
files.push(info);
}
group_by.entry(hash).or_default().push(path);
}
}
if config.verbose {
group_by
.values()
.filter(|paths| paths.len() > 1)
.for_each(|paths| {
println!("{id}: {paths:#?}\n", id = "identical files".yellow());
});
}
Ok(files)
}
pub fn shuffle<T>(vec: &mut [T]) {
let n: usize = vec.len();
for i in 0..(n - 1) {
let j = (rand() as usize) % (n - i) + i;
vec.swap(i, j);
}
}
pub fn get_random_integer(min: u64, max: u64) -> u64 {
min + rand() % (max - min + 1)
}
pub fn get_random_integer_v2(min: u64, max: u64) -> WallSwitchResult<u64> {
if min > max {
Err(WallSwitchError::MinMax { min, max })
} else {
Ok(min + rand() % (max - min + 1))
}
}
pub fn rand() -> u64 {
RandomState::new().build_hasher().finish()
}
pub fn display_files(files: &[FileInfo], config: &Config) {
let nfiles = files.len();
let ndigits = nfiles.count_chars();
if config.sort {
println!("{nfiles} images were found (sorted):");
} else {
println!("{nfiles} images were found (shuffled):");
}
for file in files.iter() {
println!(
"images[{n:0ndigits$}/{t}]: {p:?}",
n = file.number,
p = file.path,
t = file.total,
);
}
println!();
}
pub fn update_images(files: &[FileInfo], config: &Config) -> Vec<FileInfo> {
let mut owned_files: Vec<FileInfo> = files.to_vec();
thread::scope(|scope| {
for file in &mut owned_files {
scope.spawn(move || -> WallSwitchResult<()> {
file.update_info(config)
});
}
});
owned_files
}
#[cfg(test)]
mod test_lib {
use crate::*;
#[test]
fn vec_shuffle() {
let mut vec: Vec<u32> = (1..=100).collect();
shuffle(&mut vec);
println!("vec: {vec:?}");
assert_eq!(vec.len(), 100);
}
#[test]
fn random_integers_v1() {
let value: u64 = get_random_integer(1, 20);
println!("integer: {value:?}");
let integers: Vec<u64> = (0..100).map(|_| get_random_integer(1, 20)).collect();
println!("integers: {integers:?}");
let condition_a = integers.iter().min() >= Some(&1);
let condition_b = integers.iter().max() <= Some(&20);
assert!(condition_a);
assert!(condition_b);
assert_eq!(integers.len(), 100);
}
#[test]
fn random_integers_v2() -> WallSwitchResult<()> {
let value: u64 = get_random_integer_v2(1, 20)?;
println!("integer: {value:?}");
let integers: Vec<u64> = (0..100)
.map(|_| get_random_integer_v2(1, 20))
.collect::<Result<Vec<u64>, _>>()?;
println!("integers: {integers:?}");
let condition_a = integers.iter().min() >= Some(&1);
let condition_b = integers.iter().max() <= Some(&20);
assert!(condition_a);
assert!(condition_b);
assert_eq!(integers.len(), 100);
Ok(())
}
#[test]
fn random_integers_v3() -> WallSwitchResult<()> {
let result = get_random_integer_v2(21, 20).map_err(|err| {
eprintln!("{err}");
err
});
assert!(result.is_err());
let error = result.unwrap_err();
eprintln!("error: {error:?}");
Ok(())
}
}