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
#![allow(non_snake_case)]
use std::sync::{Arc, Mutex};
use std::thread;
use std::time::{Duration, SystemTime, UNIX_EPOCH};
use crate::watcher_config::{WatcherTask, WatcherConfig};
use crate::ui::state::UiMessage;
use std::sync::mpsc::Sender;
/// Starts the background watcher thread
pub fn start_watcher_thread(tasks: Arc<Mutex<Vec<WatcherTask>>>, tx: Sender<UiMessage>) {
thread::spawn(move || {
loop {
// Sleep for 60 seconds
thread::sleep(Duration::from_secs(60));
let now = SystemTime::now();
let since_epoch = now.duration_since(UNIX_EPOCH).unwrap_or(Duration::ZERO).as_secs();
// Get current local time (simple approximation or using system calls)
// Ideally we need a chrono-like functionality, but we want zero-deps.
// We'll use Win32 API to get local time.
// Manual SYSTEMTIME definition
#[repr(C)]
struct SYSTEMTIME {
wYear: u16,
wMonth: u16,
wDayOfWeek: u16,
wDay: u16,
wHour: u16,
wMinute: u16,
wSecond: u16,
wMilliseconds: u16,
}
let mut system_time: SYSTEMTIME = unsafe { std::mem::zeroed() };
// Manual binding for GetLocalTime
#[link(name = "kernel32")]
unsafe extern "system" {
fn GetLocalTime(lpsystemtime: *mut SYSTEMTIME);
}
unsafe { GetLocalTime(&mut system_time) };
let current_dow = if system_time.wDayOfWeek == 0 { 6 } else { system_time.wDayOfWeek - 1 } as u8; // Mon=0..Sun=6
let current_hour = system_time.wHour as u8;
let current_minute = system_time.wMinute as u8;
// Calculate start of today (midnight) in unix timestamp to prevent re-running
// This is tricky without chrono, so we'll rely on last_run_timestamp comparison
// If last_run_timestamp is within the last 24 hours AND matches today's date...
// Simpler: if last_run_timestamp > (now - 12h) ...
// Better: Store the last run day/hour/min? No, just timestamp.
// Strategy: if (now - last_run) > 12 hours AND current time matches schedule.
// Ideally we want to run EXACTLY once per day per schedule.
let mut tasks_guard = tasks.lock().unwrap();
let mut dirty = false;
for task in tasks_guard.iter_mut() {
// Check Day
// days_mask: Bit 0=Mon ... 6=Sun, 7=Every Day
let is_today = (task.days_mask & (1 << current_dow)) != 0 || (task.days_mask & 0x80) != 0;
if is_today {
// Check Time (trigger if current time >= schedule time within a small window, or just >= and not run today)
// We simply check if we are past the scheduled time.
let scheduled_passed = (current_hour > task.time_hour) ||
(current_hour == task.time_hour && current_minute >= task.time_minute);
if scheduled_passed {
// Check if already run recently (e.g., within last 12 hours)
// 12 hours = 43200 seconds
let time_since_last = since_epoch.saturating_sub(task.last_run_timestamp);
if time_since_last > 43200 {
// Trigger!
let path = task.get_path();
// Send to UI Main Thread to process
// We construct a special internal message or just use StatusText for now to debug
// Real implementation: We need to trigger batch processing.
// We can use `ingest_paths` if we had access to mutable AppState, but we don't here.
// We only have the Tx.
// We need a new UiMessage type for "WatcherTriggered"
// For execution, we can spawn a worker directly?
// No, UI should manage it to show progress.
// Let's assume we add `WatcherTrigger(String, WofAlgorithm)` to UiMessage.
// Log it
// Manual construction to avoid format!
let prefix = crate::w!("Watcher executing: ");
let path_w = crate::utils::to_wstring(&path);
let msg = crate::utils::concat_wstrings(&[prefix, &path_w]);
let _ = tx.send(UiMessage::StatusText(msg));
// We'll abuse `StatusText` for now if we don't want to change UiMessage enum in a big way yet,
// BUT changing UiMessage is better.
// Let's assume we can add `TriggerBatch(String, WofAlgorithm)` to `UiMessage` in `state.rs`.
// I will add that modification to state.rs next.
// Mark as run
task.last_run_timestamp = since_epoch;
dirty = true;
}
}
}
}
if dirty {
let _ = WatcherConfig::save(&tasks_guard);
}
}
});
}