Major refactor around task groups

This commit is contained in:
2Shirt 2025-05-25 01:11:24 -07:00
parent 081fd22de1
commit 2296b8f274
Signed by: 2Shirt
GPG key ID: 152FAC923B0E132C
8 changed files with 138 additions and 254 deletions

View file

@ -39,7 +39,6 @@ use core::{
tui::{Event, Tui}, tui::{Event, Tui},
}; };
use std::{ use std::{
collections::VecDeque,
env, env,
iter::zip, iter::zip,
path::PathBuf, path::PathBuf,
@ -54,7 +53,7 @@ use ratatui::{
style::Color, style::Color,
}; };
use tokio::sync::mpsc; use tokio::sync::mpsc;
use tracing::{debug, info, warn}; use tracing::{debug, info};
pub struct App { pub struct App {
// TUI // TUI
@ -76,7 +75,6 @@ pub struct App {
selections: Vec<Option<usize>>, selections: Vec<Option<usize>>,
system32: String, system32: String,
tasks: Tasks, tasks: Tasks,
task_groups: VecDeque<Option<String>>,
} }
impl App { impl App {
@ -120,7 +118,6 @@ impl App {
system32: String::new(), system32: String::new(),
selections: vec![None, None], selections: vec![None, None],
tasks, tasks,
task_groups: VecDeque::new(),
}) })
} }
@ -401,20 +398,12 @@ impl App {
self.action_tx.send(Action::Select(None, None))?; self.action_tx.send(Action::Select(None, None))?;
} }
Action::TaskGroupStart(ref title) => { Action::TaskGroupStart(ref title) => {
// TODO: Verify this isn't broken and/or unused
if self.cur_mode == Mode::BootScan { if self.cur_mode == Mode::BootScan {
info!("TaskGroup: {:?}", self.task_groups.front()); self.action_tx.send(Action::DiagLineStart {
if self.task_groups.front().is_some() { text: title.clone(),
if self.task_groups.front().unwrap().is_some() { })?;
// None here means that we're in the middle of a group of tasks self.diag_groups.start(title.to_owned());
// 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);
}
} }
} }
Action::TasksComplete => { Action::TasksComplete => {
@ -443,70 +432,22 @@ impl App {
fn handle_task(&mut self, task: &Task) -> Result<()> { fn handle_task(&mut self, task: &Task) -> Result<()> {
info!("Handling Task: {task:?}"); info!("Handling Task: {task:?}");
let title: Option<String>;
match self.cur_mode { match self.cur_mode {
Mode::BootScan => { Mode::BootScan => {
let task_group = self.task_groups.pop_front(); if let Some(result) = &task.result {
match &task.task_type { let title = self.diag_groups.current_group();
TaskType::CommandWait(cmd_path, _cmd_args) => { let passed: bool;
let mut cmd_name = ""; let info: String;
if let Some(path) = cmd_path.file_name() { match result {
if let Some(cmd_str) = path.to_str() { TaskResult::Error(msg) => {
cmd_name = cmd_str; passed = false;
} info = msg.to_owned();
}; }
let diag_type = diags::get_type(cmd_name); TaskResult::Output(stdout, stderr, success) => {
match diag_type { passed = *success;
diags::Type::Bitlocker if title == "Filesystem" {
| diags::Type::BootConfigData info = parse_chkdsk(stdout);
| diags::Type::Registry } else {
| 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;
let div = if !(stdout.is_empty() || stderr.is_empty()) { let div = if !(stdout.is_empty() || stderr.is_empty()) {
"\n\n-----------\n\n" "\n\n-----------\n\n"
} else { } else {
@ -515,37 +456,19 @@ impl App {
info = format!("{stdout}{div}{stderr}"); 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 { self.diag_groups.update(title, passed, info);
// title was not set if passed {
self.action_tx.send(Action::DiagEndLine { self.action_tx.send(Action::DiagLineUpdate {
result: DiagResult::Warn, result: DiagResult::Pass,
text: String::from("Yellow no title?"), 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 // Bitlocker
@ -647,7 +574,6 @@ impl App {
), ),
], ],
); );
self.task_groups.push_back(Some(String::from("Bitlocker")));
// Filesystem Health // Filesystem Health
let paths: Vec<PathBuf> = [ let paths: Vec<PathBuf> = [
@ -671,7 +597,6 @@ impl App {
TaskType::TestPaths(paths), TaskType::TestPaths(paths),
], ],
); );
self.task_groups.push_back(Some(String::from("Filesystem")));
// DISM Health // DISM Health
self.tasks.add_group( self.tasks.add_group(
@ -681,8 +606,6 @@ impl App {
vec![format!("{letter_os}:")], vec![format!("{letter_os}:")],
)], )],
); );
self.task_groups
.push_back(Some(String::from("System Files")));
// Registry // Registry
self.tasks.add_group( 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 self.tasks.add(TaskType::Sleep); // NOTE: DELETEME
} }
} }

View file

@ -34,6 +34,7 @@ struct ProgressLine {
name: String, name: String,
text: String, text: String,
result: DiagResult, result: DiagResult,
running: bool,
} }
impl ProgressLine { impl ProgressLine {
@ -70,17 +71,18 @@ impl Progress {
impl Component for Progress { impl Component for Progress {
fn update(&mut self, action: Action) -> Result<Option<Action>> { fn update(&mut self, action: Action) -> Result<Option<Action>> {
match action { match action {
Action::DiagStartLine { text } => { Action::DiagLineStart { text } => {
info!("Caught Action::DiagStartLine"); info!("Caught Action::DiagLineStart {{ \"{}\" }}", &text);
self.lines.push(ProgressLine { self.lines.push(ProgressLine {
name: text, name: text,
text: String::new(), text: String::from("OK"),
result: DiagResult::Pass, result: DiagResult::Pass,
running: true,
}); });
} }
Action::DiagEndLine { result, text } => { Action::DiagLineUpdate { result, text } => {
info!( info!(
"Caught Action::DiagEndLine {{ {}, \"{}\" }}", "Caught Action::DiagLineUpdate {{ {}, \"{}\" }}",
&result, &text &result, &text
); );
if let Some(line) = self.lines.last_mut() { if let Some(line) = self.lines.last_mut() {
@ -88,6 +90,12 @@ impl Component for Progress {
line.text = text; 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, Action::SetMode(mode) => self.mode = mode,
_ => {} _ => {}
}; };
@ -111,7 +119,7 @@ impl Component for Progress {
DiagResult::Fail => Color::Red, DiagResult::Fail => Color::Red,
DiagResult::Warn => Color::Yellow, DiagResult::Warn => Color::Yellow,
}; };
let text = if line.text.is_empty() { let text = if line.running || line.text.is_empty() {
String::from("...") String::from("...")
} else { } else {
line.text.clone() line.text.clone()

View file

@ -13,29 +13,9 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// 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 std::{collections::HashMap, fmt}; use std::collections::HashMap;
pub enum Type { use tracing::warn;
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"),
}
}
}
#[derive(Debug)] #[derive(Debug)]
pub struct Groups { pub struct Groups {
@ -51,8 +31,11 @@ impl Groups {
} }
} }
pub fn contains(&self, name: &str) -> bool { pub fn current_group(&self) -> String {
self.items.contains_key(name) match self.order.last() {
Some(label) => label.clone(),
None => String::from("No current group"),
}
} }
pub fn get(&self) -> Vec<&Line> { pub fn get(&self) -> Vec<&Line> {
@ -65,24 +48,24 @@ impl Groups {
lines lines
} }
pub fn update(&mut self, title: String, passed: Option<bool>, info: Option<String>) { 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) { if let Some(line) = self.items.get_mut(&title) {
line.update(passed, info); line.update(passed, info);
} else { } else {
let info_list = if info.is_some() { warn!("WARNING/DELETEME - This shouldn't happen?!");
vec![info.unwrap()] self.start(title);
} else {
Vec::new()
};
self.order.push(title.clone());
self.items.insert(
title.clone(),
Line {
title,
passed: passed.unwrap_or(true),
info: info_list,
},
);
} }
} }
} }
@ -95,34 +78,8 @@ pub struct Line {
} }
impl Line { impl Line {
pub fn update(&mut self, passed: Option<bool>, info: Option<String>) { pub fn update(&mut self, passed: bool, info: String) {
if let Some(result) = passed { self.passed &= passed; // We fail if any tests in this group fail
self.passed &= result; // We fail if any tests in this group fail self.info.push(info);
}
if let Some(info_str) = info {
self.info.push(String::from(info_str));
}
} }
} }
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
}

View file

@ -34,8 +34,9 @@ pub enum DiagResult {
pub enum Action { pub enum Action {
// App (Boot-Diags) // App (Boot-Diags)
BootScan, BootScan,
DiagStartLine { text: String }, DiagLineStart { text: String },
DiagEndLine { result: DiagResult, text: String }, DiagLineUpdate { result: DiagResult, text: String },
DiagLineEnd { text: String },
// App (Clone) // App (Clone)
Highlight(usize), Highlight(usize),
InstallDriver, InstallDriver,
@ -44,7 +45,6 @@ pub enum Action {
Select(Option<usize>, Option<usize>), // indicies for (source, dest) etc Select(Option<usize>, Option<usize>), // indicies for (source, dest) etc
SelectRight(Option<usize>, Option<usize>), // indicies for right info pane SelectRight(Option<usize>, Option<usize>), // indicies for right info pane
TaskGroupStart(String), TaskGroupStart(String),
TaskGroupEnd,
TasksComplete, TasksComplete,
UpdateDiskList(Vec<Disk>), UpdateDiskList(Vec<Disk>),
UpdateFooter(String), UpdateFooter(String),

View file

@ -51,7 +51,7 @@ impl FpsCounter {
#[must_use] #[must_use]
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
mode: String::from(""), mode: String::new(),
last_tick_update: Instant::now(), last_tick_update: Instant::now(),
tick_count: 0, tick_count: 0,
ticks_per_second: 0.0, ticks_per_second: 0.0,
@ -92,7 +92,7 @@ impl Component for FpsCounter {
fn update(&mut self, action: Action) -> Result<Option<Action>> { fn update(&mut self, action: Action) -> Result<Option<Action>> {
match action { match action {
Action::SetMode(mode) => { Action::SetMode(mode) => {
self.mode = format!("{:?}", mode); self.mode = format!("{mode:?}");
} }
Action::Render => self.render_tick()?, Action::Render => self.render_tick()?,
Action::Tick => self.app_tick()?, Action::Tick => self.app_tick()?,

View file

@ -166,7 +166,6 @@ pub fn get_fake_disks() -> Vec<Disk> {
letter: String::from("C"), letter: String::from("C"),
part_type: String::from("7"), part_type: String::from("7"),
size: 104_857_600, size: 104_857_600,
..Default::default()
}, },
Partition { Partition {
id: 2, id: 2,
@ -181,7 +180,6 @@ pub fn get_fake_disks() -> Vec<Disk> {
letter: String::from("D"), letter: String::from("D"),
part_type: String::from("7"), part_type: String::from("7"),
size: 267_701_452_800, size: 267_701_452_800,
..Default::default()
}, },
Partition { Partition {
id: 6, id: 6,
@ -190,7 +188,6 @@ pub fn get_fake_disks() -> Vec<Disk> {
letter: String::from("E"), letter: String::from("E"),
part_type: String::from("7"), part_type: String::from("7"),
size: 524_288_000, size: 524_288_000,
..Default::default()
}, },
], ],
serial: "MDZ1243".to_string(), serial: "MDZ1243".to_string(),
@ -209,7 +206,6 @@ pub fn get_fake_disks() -> Vec<Disk> {
letter: String::from("G"), letter: String::from("G"),
part_type: String::from("7"), part_type: String::from("7"),
size: 249_998_951_424, size: 249_998_951_424,
..Default::default()
}], }],
serial: "000010000".to_string(), serial: "000010000".to_string(),
size: 250_000_000_000, size: 250_000_000_000,
@ -261,7 +257,6 @@ pub fn get_fake_disks() -> Vec<Disk> {
letter: String::from("I"), letter: String::from("I"),
part_type: String::from("EFI"), part_type: String::from("EFI"),
size: 209_715_200, size: 209_715_200,
..Default::default()
}, },
Partition { Partition {
id: 2, id: 2,

View file

@ -33,57 +33,57 @@ use crate::system::disk::{
static DEFAULT_MAX_DISKS: usize = 8; static DEFAULT_MAX_DISKS: usize = 8;
struct RegexList { struct RegexList {
re_detail_all_disks: OnceLock<Regex>, detail_all_disks: OnceLock<Regex>,
re_detail_disk: OnceLock<Regex>, detail_disk: OnceLock<Regex>,
re_detail_partition: OnceLock<Regex>, detail_partition: OnceLock<Regex>,
re_disk_numbers: OnceLock<Regex>, disk_numbers: OnceLock<Regex>,
re_list_disk: OnceLock<Regex>, list_disk: OnceLock<Regex>,
re_list_partition: OnceLock<Regex>, list_partition: OnceLock<Regex>,
re_list_volumes: OnceLock<Regex>, list_volumes: OnceLock<Regex>,
re_split_all_disks: OnceLock<Regex>, split_all_disks: OnceLock<Regex>,
re_uuid: OnceLock<Regex>, re_uuid: OnceLock<Regex>,
} }
impl RegexList { impl RegexList {
pub fn detail_all_disks(&self) -> &Regex { 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() Regex::new(r"(?s)Disk (\d+) is now the selected disk.*?\r?\n\s*\r?\n(.*)").unwrap()
}) })
} }
pub fn detail_disk(&self) -> &Regex { 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() Regex::new(r"(.*?)\r?\nDisk ID\s*:\s+(.*?)\r?\nType\s*:\s+(.*?)\r?\n").unwrap()
}) })
} }
pub fn detail_partition(&self) -> &Regex { 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()) .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 { 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()) .get_or_init(|| Regex::new(r"\s+Disk\s+(\d+).*\n.*\n.*\nDisk ID:").unwrap())
} }
pub fn list_disk(&self) -> &Regex { 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()) .get_or_init(|| Regex::new(r"Disk\s+(\d+)\s+(\w+)\s+(\d+\s+\w+B)").unwrap())
} }
pub fn list_partition(&self) -> &Regex { 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()) .get_or_init(|| Regex::new(r"Partition\s+(\d+)\s+\w+\s+(\d+\s+\w+B)").unwrap())
} }
pub fn split_all_disks(&self) -> &Regex { 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()) .get_or_init(|| Regex::new(r"Disk \d+ is now the selected disk").unwrap())
} }
pub fn list_volumes(&self) -> &Regex { 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 ### Ltr Label Fs Type Size Status Info
// ---------- --- ----------- ----- ---------- ------- --------- -------- // ---------- --- ----------- ----- ---------- ------- --------- --------
// * Volume 1 S ESP FAT32 Partition 100 MB Healthy Hidden // * Volume 1 S ESP FAT32 Partition 100 MB Healthy Hidden
@ -100,14 +100,14 @@ impl RegexList {
} }
static REGEXES: RegexList = RegexList { static REGEXES: RegexList = RegexList {
re_detail_all_disks: OnceLock::new(), detail_all_disks: OnceLock::new(),
re_detail_disk: OnceLock::new(), detail_disk: OnceLock::new(),
re_detail_partition: OnceLock::new(), detail_partition: OnceLock::new(),
re_disk_numbers: OnceLock::new(), disk_numbers: OnceLock::new(),
re_list_disk: OnceLock::new(), list_disk: OnceLock::new(),
re_list_partition: OnceLock::new(), list_partition: OnceLock::new(),
re_list_volumes: OnceLock::new(), list_volumes: OnceLock::new(),
re_split_all_disks: OnceLock::new(), split_all_disks: OnceLock::new(),
re_uuid: OnceLock::new(), re_uuid: OnceLock::new(),
}; };
@ -152,7 +152,7 @@ pub fn get_partition_details(
disk_details: Option<&str>, disk_details: Option<&str>,
part_details: Option<&str>, part_details: Option<&str>,
) -> Vec<Partition> { ) -> Vec<Partition> {
let re_list_partitions = REGEXES.list_partition(); let list_partitions = REGEXES.list_partition();
let mut parts = Vec::new(); let mut parts = Vec::new();
// List partition // List partition
@ -163,7 +163,7 @@ pub fn get_partition_details(
let script = format!("select disk {disk_id}\r\nlist partition"); let script = format!("select disk {disk_id}\r\nlist partition");
contents = run_script(&script); contents = run_script(&script);
}; };
for (_, [number, size]) in re_list_partitions for (_, [number, size]) in list_partitions
.captures_iter(&contents) .captures_iter(&contents)
.map(|c| c.extract()) .map(|c| c.extract())
{ {
@ -274,8 +274,8 @@ pub fn build_get_disk_script(disk_nums: Option<Vec<&str>>) -> String {
} }
pub fn get_disks() -> Vec<Disk> { pub fn get_disks() -> Vec<Disk> {
let re_detail_all_disks = REGEXES.detail_all_disks(); let detail_all_disks = REGEXES.detail_all_disks();
let re_list_disk = REGEXES.list_disk(); let list_disk = REGEXES.list_disk();
let mut contents: String; let mut contents: String;
let mut output; let mut output;
let mut script: String; let mut script: String;
@ -317,7 +317,7 @@ pub fn get_disks() -> Vec<Disk> {
// i.e. 0, 1, 3, 4 // i.e. 0, 1, 3, 4
// For instance, this can happen if a drive is disconnected after startup // 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); 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 .captures_iter(dp_sections.remove(0)) // This is the "list disk" section
.map(|c| c.extract()) .map(|c| c.extract())
{ {
@ -334,10 +334,7 @@ pub fn get_disks() -> Vec<Disk> {
// Add Disk details // Add Disk details
let mut disks_raw: Vec<Disk> = Vec::with_capacity(DEFAULT_MAX_DISKS); let mut disks_raw: Vec<Disk> = Vec::with_capacity(DEFAULT_MAX_DISKS);
for section in dp_sections { for section in dp_sections {
for (_, [id, details]) in re_detail_all_disks for (_, [id, details]) in detail_all_disks.captures_iter(section).map(|c| c.extract()) {
.captures_iter(section)
.map(|c| c.extract())
{
if let Some(disk) = disks_map.remove(id) { if let Some(disk) = disks_map.remove(id) {
// We remove the disk from the HashMap because we're moving it to the Vec // 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)); 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 //Red Hat VirtIO SCSI Disk Device
//Disk ID: {E9CE8DFA-46B2-43C1-99BB-850C661CEE6B} //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(); 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.push(number);
} }
disk_nums disk_nums
} }
pub fn parse_partition_details(parts: &mut [Partition], contents: &str) { pub fn parse_partition_details(parts: &mut [Partition], contents: &str) {
let re_detail_partition = REGEXES.detail_partition(); let detail_partition = REGEXES.detail_partition();
let re_list_volume = REGEXES.list_volumes(); 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) .captures_iter(contents)
.map(|c| c.extract()) .map(|c| c.extract())
.enumerate() .enumerate()
@ -380,7 +377,7 @@ pub fn parse_partition_details(parts: &mut [Partition], contents: &str) {
// Volume info // Volume info
for (_, [_id, letter, label, fs_type]) in 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.label = String::from(label.trim());
part.letter = String::from(letter.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> { 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 // 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 sections = Vec::new();
let mut starts: Vec<usize> = vec![0]; let mut starts: Vec<usize> = vec![0];
let mut ends: Vec<usize> = Vec::new(); let mut ends: Vec<usize> = Vec::new();
let _: Vec<_> = re_split_all_disks let _: Vec<_> = split_all_disks
.find_iter(contents) .find_iter(contents)
.map(|m| { .map(|m| {
ends.push(m.start() - 1); ends.push(m.start() - 1);

View file

@ -53,7 +53,7 @@ pub enum TaskType {
UpdateDestDisk(usize), // (disk_index) UpdateDestDisk(usize), // (disk_index)
UpdateDiskList, UpdateDiskList,
GroupStart { label: String }, GroupStart { label: String },
GroupEnd, GroupEnd { label: String },
} }
impl fmt::Display for TaskType { impl fmt::Display for TaskType {
@ -75,7 +75,7 @@ impl fmt::Display for TaskType {
TaskType::UpdateDestDisk(_) => write!(f, "UpdateDestDisk"), TaskType::UpdateDestDisk(_) => write!(f, "UpdateDestDisk"),
TaskType::UpdateDiskList => write!(f, "UpdateDiskList"), TaskType::UpdateDiskList => write!(f, "UpdateDiskList"),
TaskType::GroupStart { label } => write!(f, "GroupStart({})", &label), 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 { self.task_list.push_back(Task::new(TaskType::GroupStart {
label: group_label.to_string(), 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(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] #[must_use]
@ -249,9 +251,13 @@ impl Tasks {
})); }));
} }
TaskType::GroupStart { ref label } => { 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 // Done
self.cur_task.replace(task); self.cur_task.replace(task);
@ -326,22 +332,21 @@ fn test_paths(
) -> JoinHandle<()> { ) -> JoinHandle<()> {
thread::spawn(move || { thread::spawn(move || {
let mut missing_paths = Vec::new(); let mut missing_paths = Vec::new();
let task_result: TaskResult; for path in path_list {
path_list.iter().for_each(|path| {
if !path.exists() { if !path.exists() {
missing_paths.push(String::from(path.to_string_lossy())); 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 // No missing paths
task_result = TaskResult::Output(String::from("OK"), String::new(), true); TaskResult::Output(String::from("OK"), String::new(), true)
} else { } else {
task_result = TaskResult::Output( TaskResult::Output(
String::from("Missing item(s)"), String::from("Missing item(s)"),
missing_paths.join(",\n"), missing_paths.join(",\n"),
false, false,
); )
}; };
let err_str = format!("Failed to send TaskResult: {:?}", &task_result); let err_str = format!("Failed to send TaskResult: {:?}", &task_result);