1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
use std::{process, thread::sleep, time::Duration};
use wallswitch::*;
/*
cargo run
cargo run -- -i 4
cargo run -- -m 4 -i 10
cargo test -- --show-output
cargo fmt --all -- --check
rustfmt src/structures.rs
cargo doc --open
cargo b -r && cargo install --path=.
*/
/// 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. Handle listing requests
if let Some(criteria) = args.list {
match criteria {
// If the user wants raw JSON state output
SortCriteria::Processed | SortCriteria::Unprocessed | SortCriteria::Cache => {
list_json_cache(&state, criteria)?;
}
// Standard table listing
_ => {
let mut images = gather_files(&config, &mut state)?;
// This will now show the "Probing image [x/y] : path"
// if dimensions are missing from wallswitch-state.json
images = update_images(&images, &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, state);
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;
}
}
println!();
// 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(())
}