Refactor tasks (again)

Avoids copying/recreating the TaskType enum (previously the Task enum).
This commit is contained in:
2Shirt 2025-02-11 21:36:00 -08:00
parent 1fab68500b
commit 72130109cb
Signed by: 2Shirt
GPG key ID: 152FAC923B0E132C
5 changed files with 96 additions and 93 deletions

View file

@ -27,7 +27,7 @@ use core::{
cpu::get_cpu_name, cpu::get_cpu_name,
drivers, drivers,
}, },
tasks::{Task, Tasks}, tasks::{TaskType, Tasks},
tui::{Event, Tui}, tui::{Event, Tui},
}; };
use std::{ use std::{
@ -178,7 +178,7 @@ impl App {
Mode::InjectDrivers | Mode::InstallDrivers => self.clone.scan_drivers(), Mode::InjectDrivers | Mode::InstallDrivers => self.clone.scan_drivers(),
Mode::ScanDisks => { Mode::ScanDisks => {
if self.tasks.idle() { if self.tasks.idle() {
self.tasks.add(Task::ScanDisks); self.tasks.add(TaskType::ScanDisks);
} }
self.action_tx.send(Action::DisplayPopup( self.action_tx.send(Action::DisplayPopup(
popup::Type::Info, popup::Type::Info,
@ -297,7 +297,7 @@ impl App {
match self.cur_mode { match self.cur_mode {
Mode::BootDiags => { Mode::BootDiags => {
// Check result of background task (if finished) // Check result of background task (if finished)
if let Some(handle) = self.tasks.poll()? { if let Some(task) = self.tasks.poll()? {
// TODO: Impl logic // TODO: Impl logic
} }
} }

View file

@ -14,7 +14,7 @@
// along with Deja-Vu. If not, see <https://www.gnu.org/licenses/>. // along with Deja-Vu. If not, see <https://www.gnu.org/licenses/>.
// //
use super::{disk::PartitionTableType, drivers::Driver}; use super::{disk::PartitionTableType, drivers::Driver};
use crate::tasks::Task; use crate::tasks::TaskType;
use color_eyre::Result; use color_eyre::Result;
use std::path::PathBuf; use std::path::PathBuf;
@ -30,11 +30,11 @@ pub fn configure_disk(
letter_os: &str, letter_os: &str,
system32: &str, system32: &str,
table_type: PartitionTableType, table_type: PartitionTableType,
) -> Vec<Task> { ) -> Vec<TaskType> {
let mut tasks = Vec::new(); let mut tasks = Vec::new();
// Create // Create
tasks.push(Task::Command( tasks.push(TaskType::Command(
PathBuf::from(format!("{system32}/bcdboot.exe")), PathBuf::from(format!("{system32}/bcdboot.exe")),
vec![ vec![
format!("{letter_os}:\\Windows"), format!("{letter_os}:\\Windows"),
@ -50,7 +50,7 @@ pub fn configure_disk(
// Update boot sector (for legacy setups) // Update boot sector (for legacy setups)
if table_type == PartitionTableType::Legacy { if table_type == PartitionTableType::Legacy {
tasks.push(Task::Command( tasks.push(TaskType::Command(
PathBuf::from(format!("{system32}/bootsect.exe")), PathBuf::from(format!("{system32}/bootsect.exe")),
vec![ vec![
String::from("/nt60"), String::from("/nt60"),
@ -71,10 +71,10 @@ pub fn configure_disk(
tasks tasks
} }
pub fn inject_driver(driver: &Driver, letter_os: &str, system32: &str) -> Result<Task> { pub fn inject_driver(driver: &Driver, letter_os: &str, system32: &str) -> Result<TaskType> {
//if let Some(driver_path_str) = driver.path.to_str() { //if let Some(driver_path_str) = driver.path.to_str() {
let driver_path = driver.path.to_str().unwrap(); let driver_path = driver.path.to_str().unwrap();
Ok(Task::Command( Ok(TaskType::Command(
PathBuf::from(format!("{system32}/dism.exe")), PathBuf::from(format!("{system32}/dism.exe")),
vec![ vec![
format!("/image:{letter_os}:\\"), format!("/image:{letter_os}:\\"),
@ -90,7 +90,7 @@ pub fn set_mode(
mode: SafeMode, mode: SafeMode,
system32: &str, system32: &str,
table_type: &PartitionTableType, table_type: &PartitionTableType,
) -> Result<Task> { ) -> Result<TaskType> {
let bcd_path = match table_type { let bcd_path = match table_type {
PartitionTableType::Guid => { PartitionTableType::Guid => {
format!("{letter_boot}:\\EFI\\Microsoft\\Boot\\BCD") format!("{letter_boot}:\\EFI\\Microsoft\\Boot\\BCD")
@ -115,7 +115,7 @@ pub fn set_mode(
cmd_args.push(String::from("minimal")); cmd_args.push(String::from("minimal"));
} }
} }
Ok(Task::Command( Ok(TaskType::Command(
PathBuf::from(format!("{system32}/bcdedit.exe")), PathBuf::from(format!("{system32}/bcdedit.exe")),
cmd_args, cmd_args,
)) ))

View file

@ -34,7 +34,7 @@ use crate::{
}; };
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub enum Task { pub enum TaskType {
Command(PathBuf, Vec<String>), // (command, args) Command(PathBuf, Vec<String>), // (command, args)
Diskpart(String), // (script_as_string) Diskpart(String), // (script_as_string)
ScanDisks, ScanDisks,
@ -44,16 +44,26 @@ pub enum Task {
} }
#[derive(Debug)] #[derive(Debug)]
pub struct TaskHandle { pub struct Task {
task: Task, pub handle: Option<JoinHandle<()>>,
handle: JoinHandle<()>, pub task_type: TaskType,
}
impl Task {
pub fn new(task_type: TaskType) -> Task {
Task {
handle: None,
task_type,
}
}
} }
#[derive(Debug)] #[derive(Debug)]
pub struct Tasks { pub struct Tasks {
action_tx: mpsc::UnboundedSender<Action>, action_tx: mpsc::UnboundedSender<Action>,
disk_list: Arc<Mutex<Vec<disk::Disk>>>, disk_list: Arc<Mutex<Vec<disk::Disk>>>,
handle: Option<TaskHandle>, cur_handle: Option<JoinHandle<()>>,
cur_task: Option<Task>,
task_list: VecDeque<Task>, task_list: VecDeque<Task>,
task_rx: mpsc::UnboundedReceiver<Action>, // Used to forward Actions from Tasks to App task_rx: mpsc::UnboundedReceiver<Action>, // Used to forward Actions from Tasks to App
task_tx: mpsc::UnboundedSender<Action>, // Used to forward Actions from Tasks to App task_tx: mpsc::UnboundedSender<Action>, // Used to forward Actions from Tasks to App
@ -68,24 +78,25 @@ impl Tasks {
Tasks { Tasks {
action_tx, action_tx,
disk_list: disk_list_arc, disk_list: disk_list_arc,
handle: None, cur_handle: None,
cur_task: None,
task_list: VecDeque::new(), task_list: VecDeque::new(),
task_rx, task_rx,
task_tx, task_tx,
} }
} }
pub fn add(&mut self, task: Task) { pub fn add(&mut self, task_type: TaskType) {
info!("Adding task: {:?}", &task); info!("Adding task: {:?}", &task_type);
self.task_list.push_back(task); self.task_list.push_back(Task::new(task_type));
} }
pub fn idle(&self) -> bool { pub fn idle(&self) -> bool {
self.handle.is_none() self.cur_handle.is_none()
} }
pub fn poll(&mut self) -> Result<Option<TaskHandle>> { pub fn poll(&mut self) -> Result<Option<Task>> {
let mut return_handle: Option<TaskHandle> = None; let mut return_task: Option<Task> = None;
// Forward any actions to main app // Forward any actions to main app
if let Ok(action) = self.task_rx.try_recv() { if let Ok(action) = self.task_rx.try_recv() {
let result = self.action_tx.send(action.clone()); let result = self.action_tx.send(action.clone());
@ -94,9 +105,13 @@ impl Tasks {
// Check status of current task (if one is running). // Check status of current task (if one is running).
// NOTE: Action::NextScreen is sent once all tasks are complete // NOTE: Action::NextScreen is sent once all tasks are complete
if let Some(task_handle) = self.handle.take() { if let Some(task_handle) = self.cur_handle.take() {
if task_handle.handle.is_finished() { if task_handle.is_finished() {
return_handle = Some(task_handle); // Need to return task with handle
if let Some(mut cur_task) = self.cur_task.take() {
cur_task.handle = Some(task_handle);
return_task = Some(cur_task);
}
if self.task_list.is_empty() { if self.task_list.is_empty() {
// No tasks remain // No tasks remain
self.task_tx.send(Action::NextScreen)?; self.task_tx.send(Action::NextScreen)?;
@ -105,85 +120,76 @@ impl Tasks {
self.start()?; self.start()?;
} }
} else { } else {
// Task not complete, return handle // TaskType not complete, return handle
self.handle = Some(task_handle); self.cur_handle.replace(task_handle);
} }
} else if !self.task_list.is_empty() { } else if !self.task_list.is_empty() {
// No current task but one is available // No current task but one is available
self.start()?; self.start()?;
} }
Ok(return_handle) Ok(return_task)
} }
pub fn start(&mut self) -> Result<()> { pub fn start(&mut self) -> Result<()> {
if let Some(task) = self.task_list.pop_front() { self.cur_task = self.task_list.pop_front();
if let Some(task) = self.cur_task.take() {
let task_tx = self.task_tx.clone(); let task_tx = self.task_tx.clone();
match task { match task.task_type {
Task::Command(cmd_path, cmd_args) => { TaskType::Command(ref cmd_path, ref cmd_args) => {
self.handle = Some(run_task_command( self.cur_handle = Some(run_task_command(
cmd_path.clone(), cmd_path.clone(),
cmd_args.clone(), cmd_args.clone(),
task_tx, task_tx,
)); ));
} }
Task::Diskpart(script) => { TaskType::Diskpart(ref script) => {
self.handle = Some(run_task_diskpart(&script, task_tx)); self.cur_handle = Some(run_task_diskpart(&script, task_tx));
} }
Task::ScanDisks => { TaskType::ScanDisks => {
let disk_list_arc = self.disk_list.clone(); let disk_list_arc = self.disk_list.clone();
// Queue UpdateDiskList for various components // Queue UpdateDiskList for various components
self.add(Task::UpdateDiskList); self.add(TaskType::UpdateDiskList);
self.handle = Some(TaskHandle { self.cur_handle = Some(thread::spawn(move || {
task: task.clone(), let mut disks = disk_list_arc.lock().unwrap();
handle: thread::spawn(move || { *disks = disk::get_disks()
let mut disks = disk_list_arc.lock().unwrap(); }));
*disks = disk::get_disks()
}),
});
} }
Task::Sleep => { TaskType::Sleep => {
self.handle = Some(TaskHandle { self.cur_handle = Some(thread::spawn(|| sleep(Duration::from_millis(250))));
task: task.clone(),
handle: thread::spawn(|| sleep(Duration::from_millis(250))),
});
} }
Task::UpdateDestDisk(index) => { TaskType::UpdateDestDisk(index) => {
self.action_tx.send(Action::DisplayPopup( self.action_tx.send(Action::DisplayPopup(
popup::Type::Info, popup::Type::Info,
String::from("Refreshing disk info"), String::from("Refreshing disk info"),
))?; ))?;
// Queue UpdateDiskList for various components // Queue UpdateDiskList for various components
self.add(Task::Sleep); self.add(TaskType::Sleep);
self.add(Task::UpdateDiskList); self.add(TaskType::UpdateDiskList);
// Update destination disk ~in-place // Update destination disk ~in-place
let disk_list_arc = self.disk_list.clone(); let disk_list_arc = self.disk_list.clone();
self.handle = Some(TaskHandle { self.cur_handle = Some(thread::spawn(move || {
task: task.clone(), let mut disks = disk_list_arc.lock().unwrap();
handle: thread::spawn(move || { let old_disk = &mut disks[index];
let mut disks = disk_list_arc.lock().unwrap(); disks[index] = disk::refresh_disk_info(old_disk);
let old_disk = &mut disks[index]; }));
disks[index] = disk::refresh_disk_info(old_disk);
}),
});
} }
Task::UpdateDiskList => { TaskType::UpdateDiskList => {
let disks = self.disk_list.lock().unwrap(); let disks = self.disk_list.lock().unwrap();
let disks_copy = disks.clone(); let disks_copy = disks.clone();
let action_tx = self.action_tx.clone(); let action_tx = self.action_tx.clone();
self.handle = Some(TaskHandle { self.cur_handle = Some(thread::spawn(move || {
handle: thread::spawn(move || { if let Err(err) = action_tx.send(Action::UpdateDiskList(disks_copy)) {
if let Err(err) = action_tx.send(Action::UpdateDiskList(disks_copy)) { panic!("Failed to send Action: {err:?}");
panic!("Failed to send Action: {err:?}"); }
} }));
}),
task: task.clone(),
});
} }
} }
// Done
self.cur_task.replace(task);
} }
Ok(()) Ok(())
} }
@ -193,9 +199,8 @@ fn run_task_command(
cmd_path: PathBuf, cmd_path: PathBuf,
cmd_args: Vec<String>, cmd_args: Vec<String>,
task_tx: mpsc::UnboundedSender<Action>, task_tx: mpsc::UnboundedSender<Action>,
) -> TaskHandle { ) -> JoinHandle<()> {
let task = Task::Command(cmd_path.clone(), cmd_args.clone()); if cfg!(windows) {
let handle = if cfg!(windows) {
thread::spawn(move || { thread::spawn(move || {
let result = Command::new(cmd_path) let result = Command::new(cmd_path)
.args(cmd_args) .args(cmd_args)
@ -227,14 +232,13 @@ fn run_task_command(
} else { } else {
// Simulate task if not running under Windows // Simulate task if not running under Windows
thread::spawn(|| sleep(Duration::from_millis(250))) thread::spawn(|| sleep(Duration::from_millis(250)))
}; }
TaskHandle { handle, task }
} }
fn run_task_diskpart(script: &str, task_tx: mpsc::UnboundedSender<Action>) -> TaskHandle { fn run_task_diskpart(script: &str, task_tx: mpsc::UnboundedSender<Action>) -> JoinHandle<()> {
let task = Task::Diskpart(String::from(script)); let task = TaskType::Diskpart(String::from(script));
let task_str = format!("{:?}", &task); let task_str = format!("{:?}", &task);
let handle = if cfg!(windows) { if cfg!(windows) {
let script = String::from(script); let script = String::from(script);
thread::spawn(move || { thread::spawn(move || {
let output = diskpart::run_script_raw(&script); let output = diskpart::run_script_raw(&script);
@ -251,6 +255,5 @@ fn run_task_diskpart(script: &str, task_tx: mpsc::UnboundedSender<Action>) -> Ta
} else { } else {
// Simulate task if not running under Windows // Simulate task if not running under Windows
thread::spawn(|| sleep(Duration::from_millis(250))) thread::spawn(|| sleep(Duration::from_millis(250)))
}; }
TaskHandle { handle, task }
} }

View file

@ -26,7 +26,7 @@ use core::{
boot, cpu::get_cpu_name, disk::PartitionTableType, diskpart::build_dest_format_script, boot, cpu::get_cpu_name, disk::PartitionTableType, diskpart::build_dest_format_script,
drivers, drivers,
}, },
tasks::{Task, Tasks}, tasks::{TaskType, Tasks},
tui::{Event, Tui}, tui::{Event, Tui},
}; };
use std::{ use std::{
@ -163,7 +163,7 @@ impl App {
Mode::ScanDisks => { Mode::ScanDisks => {
self.prev_mode = self.cur_mode; self.prev_mode = self.cur_mode;
if self.tasks.idle() { if self.tasks.idle() {
self.tasks.add(Task::ScanDisks); self.tasks.add(TaskType::ScanDisks);
} }
self.action_tx.send(Action::DisplayPopup( self.action_tx.send(Action::DisplayPopup(
popup::Type::Info, popup::Type::Info,
@ -191,7 +191,7 @@ impl App {
))?; ))?;
// (Re)Enable volume mounting // (Re)Enable volume mounting
self.tasks.add(Task::Command( self.tasks.add(TaskType::Command(
PathBuf::from(format!("{system32}/mountvol.exe")), PathBuf::from(format!("{system32}/mountvol.exe")),
vec![String::from("/e")], vec![String::from("/e")],
)); ));
@ -202,7 +202,7 @@ impl App {
if let Some(disk) = disk_list.get(disk_index) { if let Some(disk) = disk_list.get(disk_index) {
let table_type = self.clone.table_type.clone().unwrap(); let table_type = self.clone.table_type.clone().unwrap();
let diskpart_script = build_dest_format_script(disk.id, &table_type); let diskpart_script = build_dest_format_script(disk.id, &table_type);
self.tasks.add(Task::Diskpart(diskpart_script)); self.tasks.add(TaskType::Diskpart(diskpart_script));
} }
} }
} }
@ -211,12 +211,12 @@ impl App {
popup::Type::Info, popup::Type::Info,
String::from("Running Clone Tool"), String::from("Running Clone Tool"),
))?; ))?;
self.tasks.add(Task::Command( self.tasks.add(TaskType::Command(
self.config.clone_app_path.clone(), self.config.clone_app_path.clone(),
Vec::new(), Vec::new(),
)); ));
if let Some(dest_index) = self.clone.disk_index_dest { if let Some(dest_index) = self.clone.disk_index_dest {
self.tasks.add(Task::UpdateDestDisk(dest_index)); self.tasks.add(TaskType::UpdateDestDisk(dest_index));
} }
} }
Mode::PostClone => { Mode::PostClone => {

View file

@ -22,7 +22,7 @@ use core::{
config::Config, config::Config,
line::DVLine, line::DVLine,
state::Mode, state::Mode,
tasks::{Task, Tasks}, tasks::{TaskType, Tasks},
tui::{Event, Tui}, tui::{Event, Tui},
}; };
use std::{ use std::{
@ -244,7 +244,7 @@ impl App {
self.action_tx.send(Action::Select(None, None))?; self.action_tx.send(Action::Select(None, None))?;
} }
Action::OpenTerminal => { Action::OpenTerminal => {
self.tasks.add(Task::Command( self.tasks.add(TaskType::Command(
PathBuf::from("cmd.exe"), PathBuf::from("cmd.exe"),
vec![String::from("-new_console:n")], vec![String::from("-new_console:n")],
)); ));
@ -254,11 +254,11 @@ impl App {
popup::Type::Info, popup::Type::Info,
String::from("Restarting..."), String::from("Restarting..."),
))?; ))?;
self.tasks.add(Task::Command( self.tasks.add(TaskType::Command(
PathBuf::from("X:/Windows/System32/sync64.exe"), PathBuf::from("X:/Windows/System32/sync64.exe"),
vec![String::from("-r")], vec![String::from("-r")],
)); ));
self.tasks.add(Task::Command( self.tasks.add(TaskType::Command(
PathBuf::from("X:/Windows/System32/wpeutil.exe"), PathBuf::from("X:/Windows/System32/wpeutil.exe"),
vec![String::from("reboot")], vec![String::from("reboot")],
)); ));
@ -269,11 +269,11 @@ impl App {
popup::Type::Info, popup::Type::Info,
String::from("Powering off..."), String::from("Powering off..."),
))?; ))?;
self.tasks.add(Task::Command( self.tasks.add(TaskType::Command(
PathBuf::from("X:/Windows/System32/sync64.exe"), PathBuf::from("X:/Windows/System32/sync64.exe"),
vec![String::from("-r")], vec![String::from("-r")],
)); ));
self.tasks.add(Task::Command( self.tasks.add(TaskType::Command(
PathBuf::from("X:/Windows/System32/wpeutil.exe"), PathBuf::from("X:/Windows/System32/wpeutil.exe"),
vec![String::from("shutdown")], vec![String::from("shutdown")],
)); ));
@ -370,7 +370,7 @@ fn get_chunks(r: Rect) -> Vec<Rect> {
chunks chunks
} }
pub fn build_command(app: &App, tool: &Tool) -> Task { pub fn build_command(app: &App, tool: &Tool) -> TaskType {
let cmd_path: PathBuf; let cmd_path: PathBuf;
let mut cmd_args: Vec<String> = Vec::new(); let mut cmd_args: Vec<String> = Vec::new();
let start_index: usize; let start_index: usize;
@ -390,7 +390,7 @@ pub fn build_command(app: &App, tool: &Tool) -> Task {
}); });
} }
} }
Task::Command(cmd_path, cmd_args) TaskType::Command(cmd_path, cmd_args)
} }
fn build_left_items(app: &App) -> Action { fn build_left_items(app: &App) -> Action {