Move task result handling to app(s) from core
Allows different handling in deja-vu vs boot-diags
This commit is contained in:
parent
72130109cb
commit
2829fbcac1
2 changed files with 76 additions and 44 deletions
|
|
@ -33,6 +33,12 @@ use crate::{
|
||||||
system::{disk, diskpart},
|
system::{disk, diskpart},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub enum TaskResult {
|
||||||
|
Error(String),
|
||||||
|
Output(String, String, bool), // stdout, stderr, success
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub enum TaskType {
|
pub enum TaskType {
|
||||||
Command(PathBuf, Vec<String>), // (command, args)
|
Command(PathBuf, Vec<String>), // (command, args)
|
||||||
|
|
@ -46,6 +52,7 @@ pub enum TaskType {
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Task {
|
pub struct Task {
|
||||||
pub handle: Option<JoinHandle<()>>,
|
pub handle: Option<JoinHandle<()>>,
|
||||||
|
pub result: Option<TaskResult>,
|
||||||
pub task_type: TaskType,
|
pub task_type: TaskType,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -53,6 +60,7 @@ impl Task {
|
||||||
pub fn new(task_type: TaskType) -> Task {
|
pub fn new(task_type: TaskType) -> Task {
|
||||||
Task {
|
Task {
|
||||||
handle: None,
|
handle: None,
|
||||||
|
result: None,
|
||||||
task_type,
|
task_type,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -65,8 +73,8 @@ pub struct Tasks {
|
||||||
cur_handle: Option<JoinHandle<()>>,
|
cur_handle: Option<JoinHandle<()>>,
|
||||||
cur_task: Option<Task>,
|
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<TaskResult>,
|
||||||
task_tx: mpsc::UnboundedSender<Action>, // Used to forward Actions from Tasks to App
|
task_tx: mpsc::UnboundedSender<TaskResult>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Tasks {
|
impl Tasks {
|
||||||
|
|
@ -97,10 +105,12 @@ impl Tasks {
|
||||||
|
|
||||||
pub fn poll(&mut self) -> Result<Option<Task>> {
|
pub fn poll(&mut self) -> Result<Option<Task>> {
|
||||||
let mut return_task: Option<Task> = None;
|
let mut return_task: Option<Task> = None;
|
||||||
// Forward any actions to main app
|
// Handle task channel item(s)
|
||||||
if let Ok(action) = self.task_rx.try_recv() {
|
if let Ok(result) = self.task_rx.try_recv() {
|
||||||
let result = self.action_tx.send(action.clone());
|
if let Some(mut task) = self.cur_task.take() {
|
||||||
assert!(result.is_ok(), "Failed to send Action: {action:?}");
|
task.result.replace(result);
|
||||||
|
self.cur_task.replace(task);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check status of current task (if one is running).
|
// Check status of current task (if one is running).
|
||||||
|
|
@ -114,7 +124,7 @@ impl Tasks {
|
||||||
}
|
}
|
||||||
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.action_tx.send(Action::NextScreen)?;
|
||||||
} else {
|
} else {
|
||||||
// Start next task
|
// Start next task
|
||||||
self.start()?;
|
self.start()?;
|
||||||
|
|
@ -195,10 +205,17 @@ impl Tasks {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn parse_bytes_as_str(bytes: Vec<u8>) -> String {
|
||||||
|
match String::from_utf8(bytes) {
|
||||||
|
Ok(s) => s.trim().to_string(),
|
||||||
|
Err(_) => String::from("Failed to parse bytes as UTF-8 text"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn run_task_command(
|
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<TaskResult>,
|
||||||
) -> JoinHandle<()> {
|
) -> JoinHandle<()> {
|
||||||
if cfg!(windows) {
|
if cfg!(windows) {
|
||||||
thread::spawn(move || {
|
thread::spawn(move || {
|
||||||
|
|
@ -206,27 +223,19 @@ fn run_task_command(
|
||||||
.args(cmd_args)
|
.args(cmd_args)
|
||||||
.stdout(Stdio::piped())
|
.stdout(Stdio::piped())
|
||||||
.output();
|
.output();
|
||||||
if let Some(action) = match result {
|
match result {
|
||||||
Ok(output) => {
|
Err(e) => {
|
||||||
if output.status.success() {
|
task_tx
|
||||||
None
|
.send(TaskResult::Error(format!("{:?}", &e)))
|
||||||
} else {
|
.expect("Failed to propegate error?");
|
||||||
// Command returned an error status
|
}
|
||||||
let mut msg = String::new();
|
Ok(output) => {
|
||||||
if let Ok(stdout) = String::from_utf8(output.stdout) {
|
let stderr = parse_bytes_as_str(output.stderr.to_owned());
|
||||||
msg = String::from(stdout.trim());
|
let stdout = parse_bytes_as_str(output.stdout.to_owned());
|
||||||
}
|
let task_result = TaskResult::Output(stdout, stderr, output.status.success());
|
||||||
if msg.is_empty() {
|
let err_str = format!("Failed to send TaskResult: {:?}", &task_result);
|
||||||
msg = String::from("Generic error");
|
task_tx.send(task_result).expect(err_str.as_str());
|
||||||
}
|
|
||||||
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 {
|
} else {
|
||||||
|
|
@ -235,22 +244,16 @@ fn run_task_command(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_task_diskpart(script: &str, task_tx: mpsc::UnboundedSender<Action>) -> JoinHandle<()> {
|
fn run_task_diskpart(script: &str, task_tx: mpsc::UnboundedSender<TaskResult>) -> JoinHandle<()> {
|
||||||
let task = TaskType::Diskpart(String::from(script));
|
|
||||||
let task_str = format!("{:?}", &task);
|
|
||||||
if cfg!(windows) {
|
if cfg!(windows) {
|
||||||
let script = String::from(script);
|
let script = script.to_owned();
|
||||||
thread::spawn(move || {
|
thread::spawn(move || {
|
||||||
let output = diskpart::run_script_raw(&script);
|
let output = diskpart::run_script_raw(&script);
|
||||||
if !output.status.success()
|
let stderr = parse_bytes_as_str(output.stderr.to_owned());
|
||||||
&& task_tx
|
let stdout = parse_bytes_as_str(output.stdout.to_owned());
|
||||||
.send(Action::Error(String::from(
|
let task_result = TaskResult::Output(stdout, stderr, output.status.success());
|
||||||
"Diskpart script returned an error",
|
let err_str = format!("Failed to send TaskResult: {:?}", &task_result);
|
||||||
)))
|
task_tx.send(task_result).expect(err_str.as_str());
|
||||||
.is_err()
|
|
||||||
{
|
|
||||||
panic!("Failed to send Action: {task_str:?}");
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
// Simulate task if not running under Windows
|
// Simulate task if not running under Windows
|
||||||
|
|
|
||||||
|
|
@ -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::{TaskType, Tasks},
|
tasks::{TaskResult, TaskType, Tasks},
|
||||||
tui::{Event, Tui},
|
tui::{Event, Tui},
|
||||||
};
|
};
|
||||||
use std::{
|
use std::{
|
||||||
|
|
@ -375,8 +375,37 @@ impl App {
|
||||||
self.last_tick_key_events.drain(..);
|
self.last_tick_key_events.drain(..);
|
||||||
match self.cur_mode {
|
match self.cur_mode {
|
||||||
Mode::ScanDisks | Mode::PreClone | Mode::Clone | Mode::PostClone => {
|
Mode::ScanDisks | Mode::PreClone | Mode::Clone | Mode::PostClone => {
|
||||||
// Check background task
|
// Check background task (Action::NextScreen is sent when task(s) are done)
|
||||||
self.tasks.poll()?; // Once all are complete Action::NextScreen is sent
|
if let Some(task) = self.tasks.poll()? {
|
||||||
|
match task.task_type {
|
||||||
|
TaskType::Command(_, _) | TaskType::Diskpart(_) => {
|
||||||
|
if let Some(result) = &task.result {
|
||||||
|
match result {
|
||||||
|
TaskResult::Error(msg) => {
|
||||||
|
self.action_tx.send(Action::Error(format!(
|
||||||
|
"{task:?} Failed: {msg}"
|
||||||
|
)))?;
|
||||||
|
}
|
||||||
|
TaskResult::Output(stdout, stderr, success) => {
|
||||||
|
if !success {
|
||||||
|
let msg = if !stdout.is_empty() {
|
||||||
|
stdout.clone()
|
||||||
|
} else if !stderr.is_empty() {
|
||||||
|
stderr.clone()
|
||||||
|
} else {
|
||||||
|
String::from("Unknown Error")
|
||||||
|
};
|
||||||
|
self.action_tx.send(Action::Error(
|
||||||
|
format!("{task:?} Failed: {msg}"),
|
||||||
|
))?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue