Compare commits

...

2 commits

Author SHA1 Message Date
4fca7d6696
Add Bitlocker result parsing logic 2025-06-04 19:25:07 -07:00
0084b5968d
Move carriage return removal to its own function
Pretty sure I'll need it elsewhere
2025-06-04 19:24:22 -07:00
2 changed files with 99 additions and 40 deletions

View file

@ -54,7 +54,7 @@ use ratatui::{
use tokio::sync::mpsc;
use tracing::{debug, info};
use crate::diags::{DiagGroup, Type as DiagType, get_diag_type, parse_chkdsk};
use crate::diags::{DiagGroup, Type as DiagType, get_diag_type, parse_bitlocker, parse_chkdsk};
pub struct App {
// TUI
@ -445,9 +445,13 @@ impl App {
if let Ok(mut diag_groups) = self.diag_groups.lock() {
if let Some(current_group) = diag_groups.last_mut() {
match current_group.diag_type {
DiagType::Bitlocker => {
if let Some(task_result) = &task.result {
parse_bitlocker(current_group, task_result.clone());
}
}
DiagType::CheckDisk => {
if let Some(task_result) = &task.result {
//
parse_chkdsk(current_group, task_result.clone());
}
}

View file

@ -21,11 +21,21 @@ use regex::Regex;
use tracing::{info, warn};
pub struct RegexList {
bitlocker_locked: OnceLock<Regex>,
bitlocker_off: OnceLock<Regex>,
carriage_returns: OnceLock<Regex>,
chkdsk_splits: OnceLock<Regex>,
}
impl RegexList {
fn bitlocker_locked(&self) -> &Regex {
self.bitlocker_locked
.get_or_init(|| Regex::new(r"Lock Status:\s+Locked").unwrap())
}
fn bitlocker_off(&self) -> &Regex {
self.bitlocker_off
.get_or_init(|| Regex::new(r"Protection Status:\s+Protection Off").unwrap())
}
fn carriage_returns(&self) -> &Regex {
self.carriage_returns
.get_or_init(|| Regex::new(r"^.*\r").unwrap())
@ -42,6 +52,8 @@ impl RegexList {
}
static REGEXES: RegexList = RegexList {
bitlocker_locked: OnceLock::new(),
bitlocker_off: OnceLock::new(),
carriage_returns: OnceLock::new(),
chkdsk_splits: OnceLock::new(),
};
@ -130,6 +142,73 @@ pub fn get_diag_type(label: &str) -> Type {
}
}
pub fn parse_bitlocker(diag_group: &mut DiagGroup, task_result: TaskResult) {
if !cfg!(windows) {
sleep(Duration::from_millis(500));
return ();
}
let re_bitlocker_locked = REGEXES.bitlocker_locked();
let re_bitlocker_off = REGEXES.bitlocker_off();
match task_result {
TaskResult::Error(err) => {
diag_group.passed = false;
diag_group.result = String::from("Error");
diag_group.logs.push(Log {
label: String::from("Bitlocker"),
summary: vec![DVLine {
line_parts: vec![String::from("Bitlocker: "), String::from("Error")],
line_colors: vec![Color::Reset, Color::Red],
}],
raw: err,
});
}
TaskResult::Output(stdout, stderr, _) => {
let output_text = if stderr.is_empty() {
stdout.clone()
} else {
format!("{stdout}\n\n-------\n\n{stderr}")
};
let result: String;
let set_result_text = !output_text.contains("All Key Protectors");
if re_bitlocker_off.is_match(&output_text) {
diag_group.passed &= true;
result = String::from("OK");
diag_group.logs.push(Log {
label: String::from("Bitlocker"),
summary: vec![DVLine {
line_parts: vec![String::from("Bitlocker: "), String::from("OFF")],
line_colors: vec![Color::Reset, Color::Green],
}],
raw: output_text,
});
} else if re_bitlocker_locked.is_match(&output_text) {
diag_group.passed = false;
result = String::from("Locked");
diag_group.logs.push(Log {
label: String::from("Bitlocker"),
summary: vec![DVLine {
line_parts: vec![String::from("Bitlocker: "), String::from("Locked")],
line_colors: vec![Color::Reset, Color::Red],
}],
raw: output_text,
});
} else {
diag_group.passed &= true;
result = String::from("Unknown");
diag_group.logs.push(Log {
label: String::from("Bitlocker"),
summary: Vec::new(),
raw: output_text,
});
}
if set_result_text {
diag_group.result = result;
}
}
}
}
pub fn parse_chkdsk(diag_group: &mut DiagGroup, task_result: TaskResult) {
if !cfg!(windows) {
sleep(Duration::from_millis(500));
@ -149,22 +228,15 @@ pub fn parse_chkdsk(diag_group: &mut DiagGroup, task_result: TaskResult) {
});
}
TaskResult::Output(stdout, stderr, _) => {
let re_carriage_returns = REGEXES.carriage_returns();
let re_chkdsk_splits = REGEXES.chkdsk_splits();
let output_text = if stderr.is_empty() {
stdout.clone()
} else {
format!("{stdout}\n\n-------\n\n{stderr}")
};
let parsed_lines: Vec<String> = output_text
.split("\r\n")
.filter_map(|line| {
let line = re_carriage_returns.replace_all(line, "").trim().to_string();
if line.is_empty() { None } else { Some(line) }
})
.collect();
let output_text = remove_carriage_returns(&output_text);
let parsed_output = re_chkdsk_splits
.replace_all(parsed_lines.join("\n").as_str(), "\n $1")
.replace_all(output_text.as_str(), "\n $1")
.to_string();
if parsed_output.contains("Windows has scanned the file system and found no problems.")
@ -193,33 +265,16 @@ pub fn parse_chkdsk(diag_group: &mut DiagGroup, task_result: TaskResult) {
}
}
}
// let mut summary = Vec::new();
// let raw = if stderr.is_empty() {
// stdout.to_string()
// } else {
// format!("{stdout}\n\n --stderr-- \n\n{stderr}")
// };
// // TODO: Implement actual logic for result
// Log {
// label: String::from("CHKDSK"),
// raw,
// summary,
// }
// // Split lines
// let lines: Vec<_> = output.split("\r\n").collect();
//
// // Omit progress lines and unhelpful messages
// lines
// .into_iter()
// .filter(|line| {
// !(line.contains("\r")
// || line.contains("Class not registered")
// || line.contains("/F parameter")
// || line.contains("Running CHKDSK")
// || line.contains("Total duration:")
// || line.contains("Failed to transfer logged messages"))
// })
// .collect::<Vec<_>>()
// .join("\n")
}
pub fn remove_carriage_returns(text: &str) -> String {
let re_carriage_returns = REGEXES.carriage_returns();
let parsed_lines: Vec<String> = text
.split("\r\n")
.filter_map(|line| {
let line = re_carriage_returns.replace_all(line, "").trim().to_string();
if line.is_empty() { None } else { Some(line) }
})
.collect();
parsed_lines.join("\n")
}