wallswitch 0.59.3

randomly selects wallpapers for multiple monitors
Documentation
use crate::{ConcurrencyExt, Dimension, DimensionError, FileInfo, WallSwitchResult};
use blake3::Hasher;
use image::ImageReader;
use std::{
    fs::File,
    io::{BufReader, Read},
    path::PathBuf,
    thread,
};

/// Size of the buffer used for reading files during the hashing process.
/// 64 KB is an optimal balance between memory usage and disk read throughput.
const BUFFER_SIZE: usize = 64 * 1024;

/// Probes image dimensions using pure-Rust in-process header scanning.
///
/// This function uses content-based format detection (magic bytes) rather than
/// relying strictly on the file extension, making it robust against missing or
/// incorrect file extensions.
pub fn probe_image_dimension(path: &PathBuf) -> WallSwitchResult<Dimension> {
    // If opening or reading the file fails, propagate automatically as WallSwitchError::Io
    let reader = ImageReader::open(path)?.with_guessed_format()?;

    // If decoding the dimensions fails, map to DimensionError::ReadFailed with path and source context
    let (width, height) = reader
        .into_dimensions()
        .map_err(|err| DimensionError::ReadFailed {
            path: path.clone(),
            source: err,
        })?;

    Ok(Dimension {
        width: width as u64,
        height: height as u64,
    })
}

/// Computes the BLAKE3 hash of multiple files using a thread-safe parallel approach.
pub fn compute_hashes_parallel(files: &mut [FileInfo]) {
    let chunk_size = files.get_chunk_size(files.len());

    thread::scope(|scope| {
        for chunk in files.chunks_mut(chunk_size) {
            scope.spawn(move || {
                for file_info in chunk {
                    if let Ok(file) = File::open(&file_info.path) {
                        let reader = BufReader::with_capacity(BUFFER_SIZE, file);
                        match get_hash(reader) {
                            Ok(hash) => file_info.hash = hash,
                            Err(err) => {
                                eprintln!(
                                    "Failed to compute BLAKE3 hash for '{}': {}",
                                    file_info.path.display(),
                                    err
                                );
                            }
                        }
                    }
                }
            });
        }
    });
}

/// Calculates the BLAKE3 hash from any IO Reader stream.
pub fn get_hash(mut reader: impl Read) -> WallSwitchResult<String> {
    let mut hasher = Hasher::new();
    let mut buffer = [0_u8; BUFFER_SIZE];

    loop {
        let count = reader.read(&mut buffer)?;

        if count == 0 {
            break;
        }

        hasher.update(&buffer[..count]);
    }

    Ok(hasher.finalize().to_hex().to_string())
}