wallswitch 0.55.0

randomly selects wallpapers for multiple monitors
Documentation
use std::{process, thread::sleep, time::Duration};
use wallswitch::*;

/*
cargo run
cargo run -- -m 4 -i 10
cargo run --features args_v1 -- -i 4
cargo run --features args_v2 -- -i 4
cargo test -- --show-output
cargo test --features args_v2 -- --show-output
cargo fmt --all -- --check
rustfmt src/structures.rs
cargo doc --open
cargo b -r && cargo install --path=.
cargo b -r && cargo install --path=. --features args_v1
cargo b -r && cargo install --path=. --features args_v2
*/

/// Entry point of the application.
///
/// Manages the high-level flow and handles potential errors gracefully.
fn main() {
    let run_result = run(); // Execute core application logic.

    match run_result {
        Ok(_) => process::exit(0), // Exit successfully.
        Err(error) => {
            eprintln!("{error}"); // Print error message.
            process::exit(1); // Exit with failure code.
        }
    }
}

/// Core application logic: coordinates arguments, state, and execution cycles.
fn run() -> WallSwitchResult<()> {
    // 1. Parse command-line arguments as the primary source of intent
    let args = Arguments::build()?;

    // 2. Load persistent state (History and BLAKE3 hash cache) from disk
    let mut state = State::load();

    // 3. Initialize configuration by merging JSON file settings with CLI overrides
    let config = Config::new(&args)?;

    // 4. If the user requested a list, process all files and exit immediately
    if let Some(criteria) = args.list {
        let images = gather_files(&config, &mut state)?;
        list_all_images(images, criteria)?;
        process::exit(0);
    }

    // 5. Normal operation: Show startup info and clean up previous processes
    show_initial_msgs(&config)?;
    kill_other_instances(&config)?;

    // 6. Execute either a single switch or start the infinite loop
    if config.once {
        try_run_cycle(&config, &mut state)
    } else {
        loop {
            try_run_cycle(&config, &mut state)?;
        }
    }
}

/// Encapsulates logic for a single wallpaper selection and application cycle.
///
/// Finds, validates, processes, and sets images as wallpaper.
fn try_run_cycle(config: &Config, state: &mut State) -> WallSwitchResult<()> {
    let mut count: usize = 0;

    // Get random images selected from config.directories
    let images: Vec<FileInfo> = get_images(config, state)?;

    if config.verbose {
        // dbg!("Images obtained for this cycle.");
        display_files(&images, config);
    }

    let images_per_cycle = config.get_number_of_images();

    'next_chunk: for files in images.chunks_exact(images_per_cycle) {
        if !files.sizes_are_valid(config) {
            continue 'next_chunk;
        }

        let figures: Vec<FileInfo> = update_images(files, config);

        print!("{}", SliceDisplay(&figures));

        for figure in &figures {
            let dimension = figure.dimension_is_valid(config);
            let file_name = figure.name_is_valid(config);

            if !dimension || !file_name {
                continue 'next_chunk;
            }
        }

        // Aplica o Wallpaper
        set_wallpaper(&figures, config)?;
        count += 1;

        // ADICIONA AO HISTÓRICO E SALVA
        for fig in &figures {
            state.history.push(fig.path.clone());
        }
        state.save()?; // Salva no disco após trocar com sucesso!

        if config.once {
            break 'next_chunk;
        }

        sleep(Duration::from_secs(config.interval));
    }

    if count == 0 {
        return Err(WallSwitchError::InsufficientNumber);
    }

    Ok(())
}