WIP: New data structs for boot diags
This commit is contained in:
parent
121beeaa78
commit
f272b0c482
7 changed files with 217 additions and 54 deletions
|
|
@ -13,6 +13,7 @@
|
|||
// You should have received a copy of the GNU General Public License
|
||||
// along with Deja-Vu. If not, see <https://www.gnu.org/licenses/>.
|
||||
//
|
||||
use crate::diags::Groups as DiagGroups;
|
||||
use core::{
|
||||
action::Action,
|
||||
components::{
|
||||
|
|
@ -27,7 +28,7 @@ use core::{
|
|||
cpu::get_cpu_name,
|
||||
drivers,
|
||||
},
|
||||
tasks::{TaskType, Tasks},
|
||||
tasks::{Task, TaskResult, TaskType, Tasks},
|
||||
tui::{Event, Tui},
|
||||
};
|
||||
use std::{
|
||||
|
|
@ -44,7 +45,7 @@ use ratatui::{
|
|||
style::Color,
|
||||
};
|
||||
use tokio::sync::mpsc;
|
||||
use tracing::{debug, info};
|
||||
use tracing::{debug, info, warn};
|
||||
|
||||
pub struct App {
|
||||
// TUI
|
||||
|
|
@ -52,6 +53,7 @@ pub struct App {
|
|||
action_tx: mpsc::UnboundedSender<Action>,
|
||||
components: Vec<Box<dyn Component>>,
|
||||
config: Config,
|
||||
diag_groups: DiagGroups,
|
||||
frame_rate: f64,
|
||||
last_tick_key_events: Vec<KeyEvent>,
|
||||
should_quit: bool,
|
||||
|
|
@ -74,7 +76,7 @@ impl App {
|
|||
let tasks = Tasks::new(action_tx.clone(), disk_list_arc.clone());
|
||||
let mut list = StatefulList::default();
|
||||
list.set_items(vec![
|
||||
Mode::BootDiags,
|
||||
Mode::BootScan,
|
||||
Mode::BootSetup,
|
||||
Mode::InjectDrivers,
|
||||
Mode::SetBootMode,
|
||||
|
|
@ -92,6 +94,7 @@ impl App {
|
|||
Box::new(popup::Popup::new()),
|
||||
],
|
||||
config: Config::new()?,
|
||||
diag_groups: DiagGroups::new(),
|
||||
frame_rate,
|
||||
last_tick_key_events: Vec::new(),
|
||||
should_quit: false,
|
||||
|
|
@ -134,7 +137,8 @@ impl App {
|
|||
Mode::ScanDisks => Mode::SelectDisks,
|
||||
Mode::SelectDisks => Mode::SelectParts,
|
||||
Mode::SelectParts => Mode::DiagMenu,
|
||||
Mode::BootDiags | Mode::BootSetup => Mode::DiagMenu, // TODO: add Mode::ProgressReport
|
||||
Mode::BootDiags | Mode::BootSetup => Mode::DiagMenu,
|
||||
Mode::BootScan => Mode::BootDiags,
|
||||
Mode::InjectDrivers | Mode::SetBootMode => Mode::DiagMenu,
|
||||
Mode::Done => Mode::DiagMenu,
|
||||
Mode::Failed => Mode::Failed,
|
||||
|
|
@ -175,6 +179,15 @@ impl App {
|
|||
self.selections[1] = None;
|
||||
self.list.select_first_item();
|
||||
}
|
||||
Mode::BootScan => {
|
||||
if self.tasks.idle() {
|
||||
self.tasks.add(TaskType::Sleep);
|
||||
}
|
||||
self.action_tx.send(Action::DisplayPopup(
|
||||
popup::Type::Info,
|
||||
String::from("Gathering info..."),
|
||||
))?;
|
||||
}
|
||||
Mode::InjectDrivers | Mode::InstallDrivers => self.clone.scan_drivers(),
|
||||
Mode::ScanDisks => {
|
||||
if self.tasks.idle() {
|
||||
|
|
@ -294,16 +307,8 @@ impl App {
|
|||
Action::Tick => {
|
||||
self.last_tick_key_events.drain(..);
|
||||
// Check background task(s)
|
||||
match self.cur_mode {
|
||||
Mode::BootDiags => {
|
||||
if let Some(task) = self.tasks.poll()? {
|
||||
// TODO: Impl logic
|
||||
}
|
||||
}
|
||||
Mode::ScanDisks => {
|
||||
let _ = self.tasks.poll()?;
|
||||
}
|
||||
_ => {}
|
||||
if let Some(task) = self.tasks.poll()? {
|
||||
self.handle_task(&task)?;
|
||||
}
|
||||
}
|
||||
Action::Quit => self.should_quit = true,
|
||||
|
|
@ -317,6 +322,7 @@ impl App {
|
|||
.send(Action::DisplayPopup(popup::Type::Error, msg.clone()))?;
|
||||
self.action_tx.send(Action::SetMode(Mode::Failed))?;
|
||||
}
|
||||
Action::BootScan => self.action_tx.send(Action::SetMode(Mode::BootScan))?,
|
||||
Action::InstallDriver => {
|
||||
self.action_tx.send(Action::SetMode(Mode::InstallDrivers))?;
|
||||
}
|
||||
|
|
@ -407,6 +413,94 @@ impl App {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_task(&mut self, task: &Task) -> Result<()> {
|
||||
match self.cur_mode {
|
||||
Mode::BootDiags => {
|
||||
if let TaskType::Command(cmd_path, cmd_args) = &task.task_type {
|
||||
let mut cmd_name = "";
|
||||
if let Some(path) = cmd_path.file_name() {
|
||||
if let Some(cmd_str) = path.to_str() {
|
||||
cmd_name = cmd_str;
|
||||
}
|
||||
};
|
||||
match cmd_name {
|
||||
"bcdedit" => {
|
||||
// Boot config
|
||||
let title = "Boot Files";
|
||||
if let Some(result) = &task.result {
|
||||
let passed: bool;
|
||||
let info: String;
|
||||
match result {
|
||||
TaskResult::Error(msg) => {
|
||||
passed = false;
|
||||
info = msg.to_owned();
|
||||
}
|
||||
TaskResult::Output(stdout, stderr, success) => {
|
||||
passed = *success;
|
||||
let div = if !(stdout.is_empty() || stderr.is_empty()) {
|
||||
"\n\n-----------\n\n"
|
||||
} else {
|
||||
""
|
||||
};
|
||||
info = format!("{stdout}{div}{stderr}");
|
||||
}
|
||||
}
|
||||
self.diag_groups
|
||||
.update(title.to_string(), passed, info.to_owned());
|
||||
}
|
||||
}
|
||||
"manage-bde" => {
|
||||
// Bitlocker
|
||||
}
|
||||
"chkdsk" => {
|
||||
// File system
|
||||
}
|
||||
"reg" => {
|
||||
// Registry
|
||||
}
|
||||
"dir" => {
|
||||
// Files/Folders
|
||||
}
|
||||
_ => {
|
||||
warn!("Unrecognized command: {:?}", &cmd_path)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
match task.task_type {
|
||||
TaskType::Command(_, _) | TaskType::Diskpart(_) => {
|
||||
// Check result
|
||||
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}"
|
||||
)))?;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn render(&mut self, tui: &mut Tui) -> Result<()> {
|
||||
tui.draw(|frame| {
|
||||
if let [header, _body, footer, left, right, popup] = get_chunks(frame.area())[..] {
|
||||
|
|
@ -484,7 +578,8 @@ fn get_chunks(r: Rect) -> Vec<Rect> {
|
|||
|
||||
fn build_footer_string(cur_mode: Mode) -> String {
|
||||
match cur_mode {
|
||||
Mode::BootDiags | Mode::BootSetup | Mode::Home | Mode::PEMenu | Mode::ScanDisks => {
|
||||
Mode::BootDiags => String::from("(r) to refresh / (q) to quit"),
|
||||
Mode::BootScan | Mode::BootSetup | Mode::Home | Mode::PEMenu | Mode::ScanDisks => {
|
||||
String::from("(q) to quit")
|
||||
}
|
||||
Mode::InstallDrivers | Mode::InjectDrivers | Mode::SetBootMode => {
|
||||
|
|
@ -547,7 +642,7 @@ fn build_left_items(app: &App) -> Action {
|
|||
.iter()
|
||||
.for_each(|disk| items.push(disk.description.to_string()));
|
||||
}
|
||||
Mode::ScanDisks => {
|
||||
Mode::BootScan | Mode::ScanDisks => {
|
||||
select_num = 0;
|
||||
title = String::from("Processing");
|
||||
}
|
||||
|
|
@ -747,7 +842,7 @@ fn build_right_items(app: &App) -> Action {
|
|||
|
||||
fn get_mode_strings(mode: Mode) -> (String, String) {
|
||||
match mode {
|
||||
Mode::BootDiags => (
|
||||
Mode::BootScan | Mode::BootDiags => (
|
||||
String::from("Boot Diagnostics"),
|
||||
String::from("Check for common Windows boot issues"),
|
||||
),
|
||||
|
|
|
|||
56
boot_diags/src/diags.rs
Normal file
56
boot_diags/src/diags.rs
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
// This file is part of Deja-Vu.
|
||||
//
|
||||
// Deja-Vu is free software: you can redistribute it and/or modify it
|
||||
// under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Deja-Vu is distributed in the hope that it will be useful, but
|
||||
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
// See the GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Deja-Vu. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
pub struct Groups {
|
||||
items: HashMap<String, Line>,
|
||||
}
|
||||
|
||||
impl Groups {
|
||||
pub fn new() -> Self {
|
||||
Groups {
|
||||
items: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update(&mut self, title: String, passed: bool, info: String) {
|
||||
if let Some(line) = self.items.get_mut(&title) {
|
||||
line.update(passed, info);
|
||||
} else {
|
||||
self.items.insert(
|
||||
title.clone(),
|
||||
Line {
|
||||
title,
|
||||
passed,
|
||||
info: vec![info],
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Line {
|
||||
title: String,
|
||||
passed: bool,
|
||||
info: Vec<String>,
|
||||
}
|
||||
|
||||
impl Line {
|
||||
pub fn update(&mut self, passed: bool, info: String) {
|
||||
self.passed &= passed; // We fail if any tests in this group fail
|
||||
self.info.push(info);
|
||||
}
|
||||
}
|
||||
|
|
@ -20,6 +20,7 @@ use core;
|
|||
use crate::app::App;
|
||||
|
||||
mod app;
|
||||
mod diags;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<()> {
|
||||
|
|
|
|||
|
|
@ -109,6 +109,13 @@
|
|||
"<Enter>": "Process",
|
||||
"<Up>": "KeyUp",
|
||||
"<Down>": "KeyDown",
|
||||
"<r>": "BootScan",
|
||||
"<q>": "Quit",
|
||||
"<Ctrl-d>": "Quit",
|
||||
"<Ctrl-c>": "Quit",
|
||||
"<Ctrl-z>": "Suspend"
|
||||
},
|
||||
"BootScan": {
|
||||
"<q>": "Quit",
|
||||
"<Ctrl-d>": "Quit",
|
||||
"<Ctrl-c>": "Quit",
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ use crate::{components::popup::Type, line::DVLine, state::Mode, system::disk::Di
|
|||
#[derive(Debug, Clone, PartialEq, Eq, Display, Serialize, Deserialize)]
|
||||
pub enum Action {
|
||||
// App (Boot-Diags)
|
||||
// TODO: Add actions?
|
||||
BootScan,
|
||||
// App (Clone)
|
||||
Highlight(usize),
|
||||
InstallDriver,
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ pub enum Mode {
|
|||
// Boot Diags
|
||||
DiagMenu,
|
||||
BootDiags,
|
||||
BootScan,
|
||||
BootSetup,
|
||||
InjectDrivers,
|
||||
SetBootMode,
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ use core::{
|
|||
boot, cpu::get_cpu_name, disk::PartitionTableType, diskpart::build_dest_format_script,
|
||||
drivers,
|
||||
},
|
||||
tasks::{TaskResult, TaskType, Tasks},
|
||||
tasks::{Task, TaskResult, TaskType, Tasks},
|
||||
tui::{Event, Tui},
|
||||
};
|
||||
use std::{
|
||||
|
|
@ -116,6 +116,7 @@ impl App {
|
|||
| Mode::PostClone => None,
|
||||
// Invalid states
|
||||
Mode::BootDiags
|
||||
| Mode::BootScan
|
||||
| Mode::BootSetup
|
||||
| Mode::DiagMenu
|
||||
| Mode::InjectDrivers
|
||||
|
|
@ -140,6 +141,7 @@ impl App {
|
|||
Mode::Failed => Mode::Failed,
|
||||
// Invalid states
|
||||
Mode::BootDiags
|
||||
| Mode::BootScan
|
||||
| Mode::BootSetup
|
||||
| Mode::DiagMenu
|
||||
| Mode::InjectDrivers
|
||||
|
|
@ -373,41 +375,9 @@ impl App {
|
|||
match action {
|
||||
Action::Tick => {
|
||||
self.last_tick_key_events.drain(..);
|
||||
match self.cur_mode {
|
||||
Mode::ScanDisks | Mode::PreClone | Mode::Clone | Mode::PostClone => {
|
||||
// Check background task (Action::NextScreen is sent when task(s) are done)
|
||||
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}"),
|
||||
))?;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
// Check background task(s)
|
||||
if let Some(task) = self.tasks.poll()? {
|
||||
self.handle_task(&task)?;
|
||||
}
|
||||
}
|
||||
Action::Quit => self.should_quit = true,
|
||||
|
|
@ -548,6 +518,37 @@ impl App {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_task(&mut self, task: &Task) -> Result<()> {
|
||||
match task.task_type {
|
||||
TaskType::Command(_, _) | TaskType::Diskpart(_) => {
|
||||
// Check result
|
||||
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}")))?;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn render(&mut self, tui: &mut Tui) -> Result<()> {
|
||||
tui.draw(|frame| {
|
||||
if let [header, _body, footer, left, right, popup] = get_chunks(frame.area())[..] {
|
||||
|
|
@ -638,6 +639,7 @@ fn build_footer_string(cur_mode: Mode) -> String {
|
|||
Mode::Done | Mode::Failed => String::from("(Enter) or (q) to quit"),
|
||||
// Invalid states
|
||||
Mode::BootDiags
|
||||
| Mode::BootScan
|
||||
| Mode::BootSetup
|
||||
| Mode::DiagMenu
|
||||
| Mode::InjectDrivers
|
||||
|
|
@ -708,6 +710,7 @@ fn build_left_items(app: &App, cur_mode: Mode) -> Action {
|
|||
}
|
||||
// Invalid states
|
||||
Mode::BootDiags
|
||||
| Mode::BootScan
|
||||
| Mode::BootSetup
|
||||
| Mode::DiagMenu
|
||||
| Mode::InjectDrivers
|
||||
|
|
|
|||
Loading…
Reference in a new issue