Compare commits

...

2 commits

Author SHA1 Message Date
167ec52d6f
Refactor tasks
Retain info about tasks to use in app logic
2025-02-05 20:27:28 -08:00
1336c20dc2
Use clearer language in logging 2025-02-04 21:23:01 -08:00
2 changed files with 128 additions and 82 deletions

View file

@ -144,7 +144,11 @@ impl App {
} }
pub fn set_boot_mode(&mut self, boot_mode: SafeMode) { pub fn set_boot_mode(&mut self, boot_mode: SafeMode) {
info!("Setting boot mode to: {:?}", boot_mode); let new_mode = match boot_mode {
SafeMode::Disable => "Normal",
SafeMode::Enable => "Safe Mode (minimal)",
};
info!("Setting boot mode to: {new_mode}");
let disk_list = self.clone.disk_list.lock().unwrap(); let disk_list = self.clone.disk_list.lock().unwrap();
if let Some(disk_index) = self.clone.disk_index_dest { if let Some(disk_index) = self.clone.disk_index_dest {
if let Some(disk) = disk_list.get(disk_index) { if let Some(disk) = disk_list.get(disk_index) {
@ -291,9 +295,15 @@ impl App {
self.last_tick_key_events.drain(..); self.last_tick_key_events.drain(..);
// Check background task(s) // Check background task(s)
match self.cur_mode { match self.cur_mode {
Mode::BootDiags => {
// Check result of background task (if finished)
if let Some(handle) = self.tasks.poll()? {
// TODO: Impl logic
}
}
Mode::ScanDisks => { Mode::ScanDisks => {
// Check background task // Check background task (Once all are complete Action::NextScreen is sent)
self.tasks.poll()?; // Once all are complete Action::NextScreen is sent self.tasks.poll()?;
} }
_ => {} _ => {}
} }

View file

@ -43,11 +43,17 @@ pub enum Task {
UpdateDiskList, UpdateDiskList,
} }
#[derive(Debug)]
pub struct TaskHandle {
task: Task,
handle: JoinHandle<()>,
}
#[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<JoinHandle<()>>, handle: Option<TaskHandle>,
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
@ -78,7 +84,8 @@ impl Tasks {
self.handle.is_none() self.handle.is_none()
} }
pub fn poll(&mut self) -> Result<()> { pub fn poll(&mut self) -> Result<Option<TaskHandle>> {
let mut return_handle: Option<TaskHandle> = 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());
@ -87,8 +94,9 @@ 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(handle) = self.handle.take() { if let Some(task_handle) = self.handle.take() {
if handle.is_finished() { if task_handle.handle.is_finished() {
return_handle = Some(task_handle);
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)?;
@ -98,78 +106,28 @@ impl Tasks {
} }
} else { } else {
// Task not complete, return handle // Task not complete, return handle
self.handle = Some(handle); self.handle = Some(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(()) Ok(return_handle)
} }
pub fn start(&mut self) -> Result<()> { pub fn start(&mut self) -> Result<()> {
if let Some(task) = self.task_list.pop_front() { if let Some(task) = self.task_list.pop_front() {
let task_str = format!("{task:?}");
let task_tx = self.task_tx.clone(); let task_tx = self.task_tx.clone();
match task { match task {
Task::Command(ref cmd_path, ref cmd_args) => { Task::Command(cmd_path, cmd_args) => {
let cmd_path = cmd_path.clone(); self.handle = Some(run_task_command(
let cmd_args = cmd_args.clone(); cmd_path.clone(),
if cfg!(windows) { cmd_args.clone(),
self.handle = Some(thread::spawn(move || { task_tx,
let result = Command::new(cmd_path) ));
.args(cmd_args)
.stdout(Stdio::piped())
.output();
if let Some(action) = match result {
Ok(output) => {
if output.status.success() {
None
} else {
// Command returned an error status
let mut msg = String::new();
if let Ok(stdout) = String::from_utf8(output.stdout) {
msg = String::from(stdout.trim());
}
if msg.is_empty() {
msg = String::from("Generic error");
}
Some(Action::Error(format!("Command failed: {msg}",)))
}
}
Err(err) => {
Some(Action::Error(format!("Failed to run command: {err:?}")))
}
} {
let msg = format!("{:?}", &action);
let result = task_tx.send(action);
assert!(result.is_ok(), "Failed to send Action: {msg}");
}
}));
} else {
// Simulate task if not running under Windows
self.handle = Some(thread::spawn(|| sleep(Duration::from_millis(250))));
}
} }
Task::Diskpart(ref script) => { Task::Diskpart(script) => {
if cfg!(windows) { self.handle = Some(run_task_diskpart(&script, task_tx));
let script = String::from(script);
self.handle = Some(thread::spawn(move || {
let output = diskpart::run_script_raw(script.as_str());
if !output.status.success()
&& task_tx
.send(Action::Error(String::from(
"Diskpart script returned an error",
)))
.is_err()
{
panic!("Failed to send Action: {task_str:?}");
}
}));
} else {
// Simulate task if not running under Windows
self.handle = Some(thread::spawn(|| sleep(Duration::from_millis(250))));
}
} }
Task::ScanDisks => { Task::ScanDisks => {
let disk_list_arc = self.disk_list.clone(); let disk_list_arc = self.disk_list.clone();
@ -177,13 +135,19 @@ impl Tasks {
// Queue UpdateDiskList for various components // Queue UpdateDiskList for various components
self.add(Task::UpdateDiskList); self.add(Task::UpdateDiskList);
self.handle = Some(thread::spawn(move || { self.handle = Some(TaskHandle {
let mut disks = disk_list_arc.lock().unwrap(); task: task.clone(),
*disks = disk::get_disks(); handle: thread::spawn(move || {
})); let mut disks = disk_list_arc.lock().unwrap();
*disks = disk::get_disks()
}),
});
} }
Task::Sleep => { Task::Sleep => {
self.handle = Some(thread::spawn(|| sleep(Duration::from_millis(250)))); self.handle = Some(TaskHandle {
task: task.clone(),
handle: thread::spawn(|| sleep(Duration::from_millis(250))),
});
} }
Task::UpdateDestDisk(index) => { Task::UpdateDestDisk(index) => {
self.action_tx.send(Action::DisplayPopup( self.action_tx.send(Action::DisplayPopup(
@ -197,24 +161,96 @@ impl Tasks {
// 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(thread::spawn(move || { self.handle = Some(TaskHandle {
let mut disks = disk_list_arc.lock().unwrap(); task: task.clone(),
let old_disk = &mut disks[index]; handle: thread::spawn(move || {
disks[index] = disk::refresh_disk_info(old_disk); let mut disks = disk_list_arc.lock().unwrap();
})); let old_disk = &mut disks[index];
disks[index] = disk::refresh_disk_info(old_disk);
}),
});
} }
Task::UpdateDiskList => { Task::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(thread::spawn(move || { self.handle = Some(TaskHandle {
if let Err(err) = action_tx.send(Action::UpdateDiskList(disks_copy)) { handle: thread::spawn(move || {
panic!("Failed to send Action: {err:?}"); if let Err(err) = action_tx.send(Action::UpdateDiskList(disks_copy)) {
} panic!("Failed to send Action: {err:?}");
})); }
}),
task: task.clone(),
});
} }
} }
} }
Ok(()) Ok(())
} }
} }
fn run_task_command(
cmd_path: PathBuf,
cmd_args: Vec<String>,
task_tx: mpsc::UnboundedSender<Action>,
) -> TaskHandle {
let task = Task::Command(cmd_path.clone(), cmd_args.clone());
let handle = if cfg!(windows) {
thread::spawn(move || {
let result = Command::new(cmd_path)
.args(cmd_args)
.stdout(Stdio::piped())
.output();
if let Some(action) = match result {
Ok(output) => {
if output.status.success() {
None
} else {
// Command returned an error status
let mut msg = String::new();
if let Ok(stdout) = String::from_utf8(output.stdout) {
msg = String::from(stdout.trim());
}
if msg.is_empty() {
msg = String::from("Generic error");
}
Some(Action::Error(format!("Command failed: {msg}",)))
}
}
Err(err) => Some(Action::Error(format!("Failed to run command: {err:?}"))),
} {
let msg = format!("{:?}", &action);
let result = task_tx.send(action);
assert!(result.is_ok(), "Failed to send Action: {msg}");
}
})
} else {
// Simulate task if not running under Windows
thread::spawn(|| sleep(Duration::from_millis(250)))
};
TaskHandle { handle, task }
}
fn run_task_diskpart(script: &str, task_tx: mpsc::UnboundedSender<Action>) -> TaskHandle {
let task = Task::Diskpart(String::from(script));
let task_str = format!("{:?}", &task);
let handle = if cfg!(windows) {
let script = String::from(script);
thread::spawn(move || {
let output = diskpart::run_script_raw(&script);
if !output.status.success()
&& task_tx
.send(Action::Error(String::from(
"Diskpart script returned an error",
)))
.is_err()
{
panic!("Failed to send Action: {task_str:?}");
}
})
} else {
// Simulate task if not running under Windows
thread::spawn(|| sleep(Duration::from_millis(250)))
};
TaskHandle { handle, task }
}