diff --git a/boot_diags/src/app.rs b/boot_diags/src/app.rs index 9afeafd..baed950 100644 --- a/boot_diags/src/app.rs +++ b/boot_diags/src/app.rs @@ -39,7 +39,6 @@ use core::{ tui::{Event, Tui}, }; use std::{ - collections::VecDeque, env, iter::zip, path::PathBuf, @@ -54,7 +53,7 @@ use ratatui::{ style::Color, }; use tokio::sync::mpsc; -use tracing::{debug, info, warn}; +use tracing::{debug, info}; pub struct App { // TUI @@ -76,7 +75,6 @@ pub struct App { selections: Vec>, system32: String, tasks: Tasks, - task_groups: VecDeque>, } impl App { @@ -120,7 +118,6 @@ impl App { system32: String::new(), selections: vec![None, None], tasks, - task_groups: VecDeque::new(), }) } @@ -401,20 +398,12 @@ impl App { self.action_tx.send(Action::Select(None, None))?; } Action::TaskGroupStart(ref title) => { + // TODO: Verify this isn't broken and/or unused if self.cur_mode == Mode::BootScan { - info!("TaskGroup: {:?}", self.task_groups.front()); - if self.task_groups.front().is_some() { - if self.task_groups.front().unwrap().is_some() { - // None here means that we're in the middle of a group of tasks - // i.e. don't start a new diag line - self.action_tx.send(Action::DiagStartLine { - text: title.clone(), - })?; - } - } - if !self.diag_groups.contains(&title) { - self.diag_groups.update(title.to_owned(), None, None); - } + self.action_tx.send(Action::DiagLineStart { + text: title.clone(), + })?; + self.diag_groups.start(title.to_owned()); } } Action::TasksComplete => { @@ -443,70 +432,22 @@ impl App { fn handle_task(&mut self, task: &Task) -> Result<()> { info!("Handling Task: {task:?}"); - let title: Option; match self.cur_mode { Mode::BootScan => { - let task_group = self.task_groups.pop_front(); - match &task.task_type { - TaskType::CommandWait(cmd_path, _cmd_args) => { - let mut cmd_name = ""; - if let Some(path) = cmd_path.file_name() { - if let Some(cmd_str) = path.to_str() { - cmd_name = cmd_str; - } - }; - let diag_type = diags::get_type(cmd_name); - match diag_type { - diags::Type::Bitlocker - | diags::Type::BootConfigData - | diags::Type::Registry - | diags::Type::System => { - title = Some(format!("{diag_type}")); - } - diags::Type::FileSystem => { - title = None; - 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; - info = parse_chkdsk(stdout); - } - } - self.diag_groups.update( - String::from("Filesystem"), - Some(passed), - Some(info.to_owned()), - ); - } - } - diags::Type::Unknown => { - title = None; - warn!("Unrecognized command: {:?}", &cmd_path); - } - }; - } - TaskType::TestPaths(_) => { - title = Some(format!("{}", diags::Type::FileSystem)); - } - _ => title = None, - } - if let Some(title_str) = title { - 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; + if let Some(result) = &task.result { + let title = self.diag_groups.current_group(); + let passed: bool; + let info: String; + match result { + TaskResult::Error(msg) => { + passed = false; + info = msg.to_owned(); + } + TaskResult::Output(stdout, stderr, success) => { + passed = *success; + if title == "Filesystem" { + info = parse_chkdsk(stdout); + } else { let div = if !(stdout.is_empty() || stderr.is_empty()) { "\n\n-----------\n\n" } else { @@ -515,37 +456,19 @@ impl App { info = format!("{stdout}{div}{stderr}"); } } - self.diag_groups - .update(title_str, Some(passed), Some(info.to_owned())); - if let Some(group) = task_group { - if let Some(wat) = group { - info!("WAT? // {wat:?}"); - if passed { - self.action_tx.send(Action::DiagEndLine { - result: DiagResult::Pass, - text: String::from("Pass?"), - })?; - } else { - self.action_tx.send(Action::DiagEndLine { - result: DiagResult::Fail, - text: String::from("Fail?"), - })?; - }; - } - } - } else { - // If title was set but there wasn't a result - self.action_tx.send(Action::DiagEndLine { - result: DiagResult::Warn, - text: String::from("Yellow no result?"), - })?; } - } else { - // title was not set - self.action_tx.send(Action::DiagEndLine { - result: DiagResult::Warn, - text: String::from("Yellow no title?"), - })?; + self.diag_groups.update(title, passed, info); + if passed { + self.action_tx.send(Action::DiagLineUpdate { + result: DiagResult::Pass, + text: String::from("Pass?"), + })?; + } else { + self.action_tx.send(Action::DiagLineUpdate { + result: DiagResult::Fail, + text: String::from("Fail?"), + })?; + }; } } _ => { @@ -577,6 +500,11 @@ impl App { } } } + TaskType::GroupEnd { ref label } => { + self.action_tx.send(Action::DiagLineEnd { + text: label.clone(), + })?; + } _ => {} } } @@ -626,7 +554,6 @@ impl App { ], )], ); - self.task_groups.push_back(Some(String::from("Boot Files"))); } // Bitlocker @@ -647,7 +574,6 @@ impl App { ), ], ); - self.task_groups.push_back(Some(String::from("Bitlocker"))); // Filesystem Health let paths: Vec = [ @@ -671,7 +597,6 @@ impl App { TaskType::TestPaths(paths), ], ); - self.task_groups.push_back(Some(String::from("Filesystem"))); // DISM Health self.tasks.add_group( @@ -681,8 +606,6 @@ impl App { vec![format!("{letter_os}:")], )], ); - self.task_groups - .push_back(Some(String::from("System Files"))); // Registry self.tasks.add_group( @@ -726,7 +649,6 @@ impl App { ), ], ); - self.task_groups.push_back(Some(String::from("Registry"))); self.tasks.add(TaskType::Sleep); // NOTE: DELETEME } } diff --git a/boot_diags/src/components/progress.rs b/boot_diags/src/components/progress.rs index c1ad7da..a008528 100644 --- a/boot_diags/src/components/progress.rs +++ b/boot_diags/src/components/progress.rs @@ -34,6 +34,7 @@ struct ProgressLine { name: String, text: String, result: DiagResult, + running: bool, } impl ProgressLine { @@ -70,17 +71,18 @@ impl Progress { impl Component for Progress { fn update(&mut self, action: Action) -> Result> { match action { - Action::DiagStartLine { text } => { - info!("Caught Action::DiagStartLine"); + Action::DiagLineStart { text } => { + info!("Caught Action::DiagLineStart {{ \"{}\" }}", &text); self.lines.push(ProgressLine { name: text, - text: String::new(), + text: String::from("OK"), result: DiagResult::Pass, + running: true, }); } - Action::DiagEndLine { result, text } => { + Action::DiagLineUpdate { result, text } => { info!( - "Caught Action::DiagEndLine {{ {}, \"{}\" }}", + "Caught Action::DiagLineUpdate {{ {}, \"{}\" }}", &result, &text ); if let Some(line) = self.lines.last_mut() { @@ -88,6 +90,12 @@ impl Component for Progress { line.text = text; } } + Action::DiagLineEnd { text } => { + info!("Caught Action::DiagLineEnd {{ \"{}\" }}", &text); + if let Some(line) = self.lines.last_mut() { + line.running = false; + } + } Action::SetMode(mode) => self.mode = mode, _ => {} }; @@ -111,7 +119,7 @@ impl Component for Progress { DiagResult::Fail => Color::Red, DiagResult::Warn => Color::Yellow, }; - let text = if line.text.is_empty() { + let text = if line.running || line.text.is_empty() { String::from("...") } else { line.text.clone() diff --git a/boot_diags/src/diags.rs b/boot_diags/src/diags.rs index 1cc0641..a035eee 100644 --- a/boot_diags/src/diags.rs +++ b/boot_diags/src/diags.rs @@ -13,29 +13,9 @@ // You should have received a copy of the GNU General Public License // along with Deja-Vu. If not, see . -use std::{collections::HashMap, fmt}; +use std::collections::HashMap; -pub enum Type { - Bitlocker, - BootConfigData, - FileSystem, - Registry, - System, - Unknown, -} - -impl fmt::Display for Type { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Type::Bitlocker => write!(f, "Bitlocker"), - Type::BootConfigData => write!(f, "Boot Files"), - Type::FileSystem => write!(f, "Filesystem"), - Type::Registry => write!(f, "Registry"), - Type::System => write!(f, "System Files"), - Type::Unknown => write!(f, "Unknown Type"), - } - } -} +use tracing::warn; #[derive(Debug)] pub struct Groups { @@ -51,8 +31,11 @@ impl Groups { } } - pub fn contains(&self, name: &str) -> bool { - self.items.contains_key(name) + pub fn current_group(&self) -> String { + match self.order.last() { + Some(label) => label.clone(), + None => String::from("No current group"), + } } pub fn get(&self) -> Vec<&Line> { @@ -65,24 +48,24 @@ impl Groups { lines } - pub fn update(&mut self, title: String, passed: Option, info: Option) { + pub fn start(&mut self, title: String) { + self.order.push(title.clone()); + self.items.insert( + title.clone(), + Line { + title, + passed: true, + info: Vec::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 { - let info_list = if info.is_some() { - vec![info.unwrap()] - } else { - Vec::new() - }; - self.order.push(title.clone()); - self.items.insert( - title.clone(), - Line { - title, - passed: passed.unwrap_or(true), - info: info_list, - }, - ); + warn!("WARNING/DELETEME - This shouldn't happen?!"); + self.start(title); } } } @@ -95,34 +78,8 @@ pub struct Line { } impl Line { - pub fn update(&mut self, passed: Option, info: Option) { - if let Some(result) = passed { - self.passed &= result; // We fail if any tests in this group fail - } - if let Some(info_str) = info { - self.info.push(String::from(info_str)); - } + 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); } } - -pub fn get_type(cmd_name: &str) -> Type { - if cmd_name.ends_with("exa") { - return Type::BootConfigData; - } - if cmd_name.ends_with("bcdedit.exe") { - return Type::BootConfigData; - } - if cmd_name.ends_with("dism.exe") { - return Type::System; - } - if cmd_name.ends_with("reg.exe") { - return Type::Registry; - } - if cmd_name.ends_with("chkdsk.exe") { - return Type::FileSystem; - } - if cmd_name.ends_with("manage-bde.exe") { - return Type::Bitlocker; - } - Type::Unknown -} diff --git a/core/src/action.rs b/core/src/action.rs index 8f9ace5..4a2c7d4 100644 --- a/core/src/action.rs +++ b/core/src/action.rs @@ -34,8 +34,9 @@ pub enum DiagResult { pub enum Action { // App (Boot-Diags) BootScan, - DiagStartLine { text: String }, - DiagEndLine { result: DiagResult, text: String }, + DiagLineStart { text: String }, + DiagLineUpdate { result: DiagResult, text: String }, + DiagLineEnd { text: String }, // App (Clone) Highlight(usize), InstallDriver, @@ -44,7 +45,6 @@ pub enum Action { Select(Option, Option), // indicies for (source, dest) etc SelectRight(Option, Option), // indicies for right info pane TaskGroupStart(String), - TaskGroupEnd, TasksComplete, UpdateDiskList(Vec), UpdateFooter(String), diff --git a/core/src/components/fps.rs b/core/src/components/fps.rs index 6fb5d9f..a8a6dcf 100644 --- a/core/src/components/fps.rs +++ b/core/src/components/fps.rs @@ -51,7 +51,7 @@ impl FpsCounter { #[must_use] pub fn new() -> Self { Self { - mode: String::from(""), + mode: String::new(), last_tick_update: Instant::now(), tick_count: 0, ticks_per_second: 0.0, @@ -92,7 +92,7 @@ impl Component for FpsCounter { fn update(&mut self, action: Action) -> Result> { match action { Action::SetMode(mode) => { - self.mode = format!("{:?}", mode); + self.mode = format!("{mode:?}"); } Action::Render => self.render_tick()?, Action::Tick => self.app_tick()?, diff --git a/core/src/system/disk.rs b/core/src/system/disk.rs index 315a800..bcf01c8 100644 --- a/core/src/system/disk.rs +++ b/core/src/system/disk.rs @@ -166,7 +166,6 @@ pub fn get_fake_disks() -> Vec { letter: String::from("C"), part_type: String::from("7"), size: 104_857_600, - ..Default::default() }, Partition { id: 2, @@ -181,7 +180,6 @@ pub fn get_fake_disks() -> Vec { letter: String::from("D"), part_type: String::from("7"), size: 267_701_452_800, - ..Default::default() }, Partition { id: 6, @@ -190,7 +188,6 @@ pub fn get_fake_disks() -> Vec { letter: String::from("E"), part_type: String::from("7"), size: 524_288_000, - ..Default::default() }, ], serial: "MDZ1243".to_string(), @@ -209,7 +206,6 @@ pub fn get_fake_disks() -> Vec { letter: String::from("G"), part_type: String::from("7"), size: 249_998_951_424, - ..Default::default() }], serial: "000010000".to_string(), size: 250_000_000_000, @@ -261,7 +257,6 @@ pub fn get_fake_disks() -> Vec { letter: String::from("I"), part_type: String::from("EFI"), size: 209_715_200, - ..Default::default() }, Partition { id: 2, diff --git a/core/src/system/diskpart.rs b/core/src/system/diskpart.rs index b3bcab8..7a62bc5 100644 --- a/core/src/system/diskpart.rs +++ b/core/src/system/diskpart.rs @@ -33,57 +33,57 @@ use crate::system::disk::{ static DEFAULT_MAX_DISKS: usize = 8; struct RegexList { - re_detail_all_disks: OnceLock, - re_detail_disk: OnceLock, - re_detail_partition: OnceLock, - re_disk_numbers: OnceLock, - re_list_disk: OnceLock, - re_list_partition: OnceLock, - re_list_volumes: OnceLock, - re_split_all_disks: OnceLock, + detail_all_disks: OnceLock, + detail_disk: OnceLock, + detail_partition: OnceLock, + disk_numbers: OnceLock, + list_disk: OnceLock, + list_partition: OnceLock, + list_volumes: OnceLock, + split_all_disks: OnceLock, re_uuid: OnceLock, } impl RegexList { pub fn detail_all_disks(&self) -> &Regex { - self.re_detail_all_disks.get_or_init(|| { + self.detail_all_disks.get_or_init(|| { Regex::new(r"(?s)Disk (\d+) is now the selected disk.*?\r?\n\s*\r?\n(.*)").unwrap() }) } pub fn detail_disk(&self) -> &Regex { - self.re_detail_disk.get_or_init(|| { + self.detail_disk.get_or_init(|| { Regex::new(r"(.*?)\r?\nDisk ID\s*:\s+(.*?)\r?\nType\s*:\s+(.*?)\r?\n").unwrap() }) } pub fn detail_partition(&self) -> &Regex { - self.re_detail_partition + self.detail_partition .get_or_init(|| Regex::new(r"Partition (\d+)\r?\nType\s*: (\S+)(\r?\n.*){5}\s*(Volume.*\r?\n.*\r?\n|There is no volume)(.*)").unwrap()) } pub fn disk_numbers(&self) -> &Regex { - self.re_disk_numbers + self.disk_numbers .get_or_init(|| Regex::new(r"\s+Disk\s+(\d+).*\n.*\n.*\nDisk ID:").unwrap()) } pub fn list_disk(&self) -> &Regex { - self.re_list_disk + self.list_disk .get_or_init(|| Regex::new(r"Disk\s+(\d+)\s+(\w+)\s+(\d+\s+\w+B)").unwrap()) } pub fn list_partition(&self) -> &Regex { - self.re_list_partition + self.list_partition .get_or_init(|| Regex::new(r"Partition\s+(\d+)\s+\w+\s+(\d+\s+\w+B)").unwrap()) } pub fn split_all_disks(&self) -> &Regex { - self.re_split_all_disks + self.split_all_disks .get_or_init(|| Regex::new(r"Disk \d+ is now the selected disk").unwrap()) } pub fn list_volumes(&self) -> &Regex { - self.re_list_volumes.get_or_init(|| { + self.list_volumes.get_or_init(|| { // Volume ### Ltr Label Fs Type Size Status Info // ---------- --- ----------- ----- ---------- ------- --------- -------- // * Volume 1 S ESP FAT32 Partition 100 MB Healthy Hidden @@ -100,14 +100,14 @@ impl RegexList { } static REGEXES: RegexList = RegexList { - re_detail_all_disks: OnceLock::new(), - re_detail_disk: OnceLock::new(), - re_detail_partition: OnceLock::new(), - re_disk_numbers: OnceLock::new(), - re_list_disk: OnceLock::new(), - re_list_partition: OnceLock::new(), - re_list_volumes: OnceLock::new(), - re_split_all_disks: OnceLock::new(), + detail_all_disks: OnceLock::new(), + detail_disk: OnceLock::new(), + detail_partition: OnceLock::new(), + disk_numbers: OnceLock::new(), + list_disk: OnceLock::new(), + list_partition: OnceLock::new(), + list_volumes: OnceLock::new(), + split_all_disks: OnceLock::new(), re_uuid: OnceLock::new(), }; @@ -152,7 +152,7 @@ pub fn get_partition_details( disk_details: Option<&str>, part_details: Option<&str>, ) -> Vec { - let re_list_partitions = REGEXES.list_partition(); + let list_partitions = REGEXES.list_partition(); let mut parts = Vec::new(); // List partition @@ -163,7 +163,7 @@ pub fn get_partition_details( let script = format!("select disk {disk_id}\r\nlist partition"); contents = run_script(&script); }; - for (_, [number, size]) in re_list_partitions + for (_, [number, size]) in list_partitions .captures_iter(&contents) .map(|c| c.extract()) { @@ -274,8 +274,8 @@ pub fn build_get_disk_script(disk_nums: Option>) -> String { } pub fn get_disks() -> Vec { - let re_detail_all_disks = REGEXES.detail_all_disks(); - let re_list_disk = REGEXES.list_disk(); + let detail_all_disks = REGEXES.detail_all_disks(); + let list_disk = REGEXES.list_disk(); let mut contents: String; let mut output; let mut script: String; @@ -317,7 +317,7 @@ pub fn get_disks() -> Vec { // i.e. 0, 1, 3, 4 // For instance, this can happen if a drive is disconnected after startup let mut disks_map: HashMap<&str, Disk> = HashMap::with_capacity(DEFAULT_MAX_DISKS); - for (_, [number, _status, size]) in re_list_disk + for (_, [number, _status, size]) in list_disk .captures_iter(dp_sections.remove(0)) // This is the "list disk" section .map(|c| c.extract()) { @@ -334,10 +334,7 @@ pub fn get_disks() -> Vec { // Add Disk details let mut disks_raw: Vec = Vec::with_capacity(DEFAULT_MAX_DISKS); for section in dp_sections { - for (_, [id, details]) in re_detail_all_disks - .captures_iter(section) - .map(|c| c.extract()) - { + for (_, [id, details]) in detail_all_disks.captures_iter(section).map(|c| c.extract()) { if let Some(disk) = disks_map.remove(id) { // We remove the disk from the HashMap because we're moving it to the Vec let mut disk = get_disk_details(disk.id, disk.size, Some(details)); @@ -357,19 +354,19 @@ pub fn parse_disk_numbers(contents: &str) -> Vec<&str> { // //Red Hat VirtIO SCSI Disk Device //Disk ID: {E9CE8DFA-46B2-43C1-99BB-850C661CEE6B} - let re_disk_numbers = REGEXES.disk_numbers(); + let disk_numbers = REGEXES.disk_numbers(); let mut disk_nums = Vec::new(); - for (_, [number]) in re_disk_numbers.captures_iter(contents).map(|c| c.extract()) { + for (_, [number]) in disk_numbers.captures_iter(contents).map(|c| c.extract()) { disk_nums.push(number); } disk_nums } pub fn parse_partition_details(parts: &mut [Partition], contents: &str) { - let re_detail_partition = REGEXES.detail_partition(); - let re_list_volume = REGEXES.list_volumes(); + let detail_partition = REGEXES.detail_partition(); + let list_volume = REGEXES.list_volumes(); - for (part_index, (_, [_part_id, part_type, _, _vol_header, vol_line])) in re_detail_partition + for (part_index, (_, [_part_id, part_type, _, _vol_header, vol_line])) in detail_partition .captures_iter(contents) .map(|c| c.extract()) .enumerate() @@ -380,7 +377,7 @@ pub fn parse_partition_details(parts: &mut [Partition], contents: &str) { // Volume info for (_, [_id, letter, label, fs_type]) in - re_list_volume.captures_iter(vol_line).map(|c| c.extract()) + list_volume.captures_iter(vol_line).map(|c| c.extract()) { part.label = String::from(label.trim()); part.letter = String::from(letter.trim()); @@ -425,11 +422,11 @@ pub fn run_script(script: &str) -> String { pub fn split_diskpart_disk_output(contents: &str) -> Vec<&str> { // NOTE: A simple split isn't helpful since we want to include the matching lines - let re_split_all_disks = REGEXES.split_all_disks(); + let split_all_disks = REGEXES.split_all_disks(); let mut sections = Vec::new(); let mut starts: Vec = vec![0]; let mut ends: Vec = Vec::new(); - let _: Vec<_> = re_split_all_disks + let _: Vec<_> = split_all_disks .find_iter(contents) .map(|m| { ends.push(m.start() - 1); diff --git a/core/src/tasks.rs b/core/src/tasks.rs index f0ddd65..a548e34 100644 --- a/core/src/tasks.rs +++ b/core/src/tasks.rs @@ -53,7 +53,7 @@ pub enum TaskType { UpdateDestDisk(usize), // (disk_index) UpdateDiskList, GroupStart { label: String }, - GroupEnd, + GroupEnd { label: String }, } impl fmt::Display for TaskType { @@ -75,7 +75,7 @@ impl fmt::Display for TaskType { TaskType::UpdateDestDisk(_) => write!(f, "UpdateDestDisk"), TaskType::UpdateDiskList => write!(f, "UpdateDiskList"), TaskType::GroupStart { label } => write!(f, "GroupStart({})", &label), - TaskType::GroupEnd => write!(f, "GroupEnd"), + TaskType::GroupEnd { label } => write!(f, "GroupEnd({})", &label), } } } @@ -136,10 +136,12 @@ impl Tasks { self.task_list.push_back(Task::new(TaskType::GroupStart { label: group_label.to_string(), })); - group_tasks.into_iter().for_each(|task| { + for task in group_tasks { self.task_list.push_back(Task::new(task)); - }); - self.task_list.push_back(Task::new(TaskType::GroupEnd)); + } + self.task_list.push_back(Task::new(TaskType::GroupEnd { + label: group_label.to_string(), + })); } #[must_use] @@ -249,9 +251,13 @@ impl Tasks { })); } TaskType::GroupStart { ref label } => { - self.action_tx.send(Action::TaskGroupStart(label.clone()))? + self.action_tx.send(Action::TaskGroupStart(label.clone()))?; + } + TaskType::GroupEnd { ref label } => { + self.action_tx.send(Action::DiagLineEnd { + text: label.clone(), + })?; } - TaskType::GroupEnd => self.action_tx.send(Action::TaskGroupEnd)?, } // Done self.cur_task.replace(task); @@ -326,22 +332,21 @@ fn test_paths( ) -> JoinHandle<()> { thread::spawn(move || { let mut missing_paths = Vec::new(); - let task_result: TaskResult; - path_list.iter().for_each(|path| { + for path in path_list { if !path.exists() { missing_paths.push(String::from(path.to_string_lossy())); } - }); + } - if missing_paths.is_empty() { + let task_result = if missing_paths.is_empty() { // No missing paths - task_result = TaskResult::Output(String::from("OK"), String::new(), true); + TaskResult::Output(String::from("OK"), String::new(), true) } else { - task_result = TaskResult::Output( + TaskResult::Output( String::from("Missing item(s)"), missing_paths.join(",\n"), false, - ); + ) }; let err_str = format!("Failed to send TaskResult: {:?}", &task_result);