Skip to main content

launcher/
tasks.rs

1use std::sync::mpsc::{Receiver, SendError, Sender, channel};
2use supreme_fs::SnapshotEvent;
3
4#[derive(Debug, Clone)]
5pub enum TaskEvent<T> {
6    Log(String),
7    SetStatus(String),
8    InitProgress(usize),
9    IncrementProgress,
10    UpdateProgress(usize),
11    Perf(String, String, u128), // Operation, Target, Duration_ms
12    PageData(T),                // Page-specific data
13    Finished(Result<T, String>),
14}
15
16impl<T> TaskEvent<T> {
17    pub fn map<U, F>(self, page_data_transform: F) -> TaskEvent<U>
18    where
19        F: FnOnce(T) -> U,
20    {
21        match self {
22            TaskEvent::Log(msg) => TaskEvent::Log(msg),
23            TaskEvent::SetStatus(msg) => TaskEvent::SetStatus(msg),
24            TaskEvent::InitProgress(total) => TaskEvent::InitProgress(total),
25            TaskEvent::IncrementProgress => TaskEvent::IncrementProgress,
26            TaskEvent::UpdateProgress(p) => TaskEvent::UpdateProgress(p),
27            TaskEvent::Perf(op, target, ms) => TaskEvent::Perf(op, target, ms),
28            TaskEvent::PageData(data) => TaskEvent::PageData(page_data_transform(data)),
29            TaskEvent::Finished(res) => TaskEvent::Finished(res.map(page_data_transform)),
30        }
31    }
32}
33
34/// A wrapper around `Sender<TaskEvent<T>>` that automatically calls
35/// `ctx.request_repaint()` on the UI context whenever an event is queued.
36pub struct TaskSender<T> {
37    tx: Sender<TaskEvent<T>>,
38    ctx: eframe::egui::Context,
39}
40
41// Custom Clone implementation to avoid requiring T: Clone
42impl<T> Clone for TaskSender<T> {
43    fn clone(&self) -> Self {
44        Self {
45            tx: self.tx.clone(),
46            ctx: self.ctx.clone(),
47        }
48    }
49}
50
51impl<T> TaskSender<T> {
52    pub fn send(&self, event: TaskEvent<T>) -> Result<(), SendError<TaskEvent<T>>> {
53        let res = self.tx.send(event);
54        self.ctx.request_repaint();
55        res
56    }
57}
58
59pub struct TaskHandle<T> {
60    pub receiver: Receiver<TaskEvent<T>>,
61}
62
63impl<T> TaskHandle<T> {
64    pub fn try_recv(&self) -> Result<TaskEvent<T>, std::sync::mpsc::TryRecvError> {
65        self.receiver.try_recv()
66    }
67}
68
69pub fn spawn_task<T, F>(ctx: eframe::egui::Context, f: F) -> TaskHandle<T>
70where
71    T: Send + 'static,
72    F: FnOnce(TaskSender<T>) -> Result<T, String> + Send + 'static,
73{
74    let (tx, rx) = channel();
75    let task_sender = TaskSender { tx, ctx };
76
77    // We clone task_sender for the Finished event send.
78    let task_sender_clone = task_sender.clone();
79
80    std::thread::Builder::new()
81        .name("launcher-task".to_string())
82        .spawn(move || {
83            let res = f(task_sender);
84            let _ = task_sender_clone.send(TaskEvent::Finished(res));
85        })
86        .expect("Failed to spawn OS thread for launcher task");
87    TaskHandle { receiver: rx }
88}
89
90/// Helper to convert `SnapshotEvent` to `TaskEvent`<()>
91impl From<SnapshotEvent> for TaskEvent<()> {
92    fn from(event: SnapshotEvent) -> Self {
93        match event {
94            SnapshotEvent::Log(msg) => TaskEvent::Log(msg),
95            SnapshotEvent::SetStatus(msg) => TaskEvent::SetStatus(msg),
96            SnapshotEvent::InitProgress(total) => TaskEvent::InitProgress(total),
97            SnapshotEvent::IncrementProgress => TaskEvent::IncrementProgress,
98            SnapshotEvent::Perf(op, target, ms) => TaskEvent::Perf(op, target, ms),
99        }
100    }
101}
102
103/// Helper to convert `CleanerEvent` to `TaskEvent`<()>
104impl From<supreme_cleaner::CleanerEvent> for TaskEvent<()> {
105    fn from(event: supreme_cleaner::CleanerEvent) -> Self {
106        match event {
107            supreme_cleaner::CleanerEvent::Log(msg) => TaskEvent::Log(msg),
108            supreme_cleaner::CleanerEvent::SetStatus(msg) => TaskEvent::SetStatus(msg),
109            supreme_cleaner::CleanerEvent::InitProgress(total) => TaskEvent::InitProgress(total),
110            supreme_cleaner::CleanerEvent::IncrementProgress => TaskEvent::IncrementProgress,
111            supreme_cleaner::CleanerEvent::Perf(op, target, ms) => TaskEvent::Perf(op, target, ms),
112        }
113    }
114}
115
116/// Helper to convert DiagnosticsEvent to TaskEvent<()>
117#[cfg(feature = "local")]
118impl From<supreme_diagnostics::DiagnosticsEvent> for TaskEvent<()> {
119    fn from(event: supreme_diagnostics::DiagnosticsEvent) -> Self {
120        match event {
121            supreme_diagnostics::DiagnosticsEvent::Log(msg) => TaskEvent::Log(msg),
122            supreme_diagnostics::DiagnosticsEvent::SetStatus(msg) => TaskEvent::SetStatus(msg),
123            supreme_diagnostics::DiagnosticsEvent::InitProgress(total) => {
124                TaskEvent::InitProgress(total)
125            }
126            supreme_diagnostics::DiagnosticsEvent::IncrementProgress => {
127                TaskEvent::IncrementProgress
128            }
129            supreme_diagnostics::DiagnosticsEvent::Perf(op, target, ms) => {
130                TaskEvent::Perf(op, target, ms)
131            }
132            supreme_diagnostics::DiagnosticsEvent::Finished(_) => {
133                // Return something neutral for finished; handled by side-channel or result T
134                TaskEvent::Log("Scanning finished".to_string())
135            }
136        }
137    }
138}
139
140/// Helper to convert `DependencyEvent` to `TaskEvent`<()>
141impl From<supreme_dependencies::DependencyEvent> for TaskEvent<()> {
142    fn from(event: supreme_dependencies::DependencyEvent) -> Self {
143        match event {
144            supreme_dependencies::DependencyEvent::Log(msg) => TaskEvent::Log(msg),
145            supreme_dependencies::DependencyEvent::SetStatus(msg) => TaskEvent::SetStatus(msg),
146            supreme_dependencies::DependencyEvent::InitProgress(total) => {
147                TaskEvent::InitProgress(total)
148            }
149            supreme_dependencies::DependencyEvent::UpdateProgress(p) => {
150                TaskEvent::UpdateProgress(p)
151            }
152            supreme_dependencies::DependencyEvent::Finished(err) => {
153                TaskEvent::Finished(err.map_or(Ok(()), Err))
154            }
155        }
156    }
157}