Compare commits

...

3 commits

2 changed files with 208 additions and 14 deletions

View file

@ -14,7 +14,7 @@
// along with Deja-Vu. If not, see <https://www.gnu.org/licenses/>.
//
use core::{
action::Action,
action::{Action, DiagResult},
components::{
Component,
footer::Footer,
@ -54,7 +54,10 @@ use ratatui::{
use tokio::sync::mpsc;
use tracing::{debug, info};
use crate::diags::{DiagGroup, Type as DiagType, get_diag_type, parse_bitlocker, parse_chkdsk};
use crate::diags::{
DiagGroup, Type as DiagType, get_diag_type, parse_bcd, parse_bitlocker, parse_chkdsk,
parse_dism, parse_system_files,
};
pub struct App {
// TUI
@ -450,13 +453,32 @@ impl App {
parse_bitlocker(current_group, task_result.clone());
}
}
DiagType::BootConfigData => {
if let Some(task_result) = &task.result {
parse_bcd(current_group, task_result.clone());
}
}
DiagType::CheckDisk => {
if let Some(task_result) = &task.result {
parse_chkdsk(current_group, task_result.clone());
}
}
DiagType::ComponentStore => {
if let Some(task_result) = &task.result {
parse_dism(current_group, task_result.clone());
}
}
DiagType::SystemFiles => {
if let Some(task_result) = &task.result {
parse_system_files(current_group, task_result.clone());
}
}
_ => (),
}
self.action_tx.send(Action::DiagLineUpdate {
result: current_group.get_pass_fail_warn(),
text: current_group.result.clone(),
})?;
}
}
return Ok(());
@ -576,7 +598,11 @@ impl App {
DiagType::ComponentStore.to_string().as_str(),
vec![TaskType::CommandWait(
PathBuf::from(format!("{}\\dism.exe", &self.system32)),
vec![format!("{letter_os}:")],
vec![
format!("/Image:{letter_os}:"),
String::from("/Cleanup-Image"),
String::from("/ScanHealth"),
],
)],
);
@ -833,7 +859,7 @@ fn build_left_items(app: &App) -> Action {
.iter()
.map(|group| {
let label = group.diag_type.to_string();
let status = if group.passed {
let status = if group.get_pass_fail_warn() == DiagResult::Pass {
"" // Leave blank if OK
} else {
" -- Issue(s) detected"

View file

@ -13,7 +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 core::{line::DVLine, tasks::TaskResult};
use core::{action::DiagResult, line::DVLine, tasks::TaskResult};
use std::{fmt, sync::OnceLock, thread::sleep, time::Duration};
use ratatui::style::Color;
@ -93,7 +93,7 @@ pub struct Log {
#[derive(Clone, Debug)]
pub struct DiagGroup {
pub diag_type: Type,
pub passed: bool,
pub passed: Vec<bool>,
pub logs: Vec<Log>,
pub result: String,
}
@ -102,7 +102,7 @@ impl DiagGroup {
pub fn new(diag_type: Type) -> Self {
DiagGroup {
diag_type,
passed: true,
passed: Vec::new(),
logs: Vec::new(),
result: String::new(),
}
@ -124,6 +124,18 @@ impl DiagGroup {
.for_each(|log| summaries.extend(log.summary.clone().into_iter()));
summaries
}
pub fn get_pass_fail_warn(&self) -> DiagResult {
let all_passed = self.passed.iter().fold(true, |acc, result| acc && *result);
let all_failed = self.passed.iter().fold(true, |acc, result| acc && !*result);
if all_passed {
DiagResult::Pass
} else if all_failed {
DiagResult::Fail
} else {
DiagResult::Warn
}
}
}
pub fn get_diag_type(label: &str) -> Type {
@ -142,6 +154,58 @@ pub fn get_diag_type(label: &str) -> Type {
}
}
pub fn parse_bcd(diag_group: &mut DiagGroup, task_result: TaskResult) {
if !cfg!(windows) {
sleep(Duration::from_millis(500));
return ();
}
match task_result {
TaskResult::Error(err) => {
diag_group.passed.push(false);
diag_group.result = String::from("Error");
diag_group.logs.push(Log {
label: String::from("BCD"),
summary: vec![DVLine {
line_parts: vec![String::from("BCD: "), String::from("Error")],
line_colors: vec![Color::Reset, Color::Red],
}],
raw: err,
});
}
TaskResult::Output(stdout, stderr, passed) => {
let output_text = if stderr.is_empty() {
stdout.clone()
} else {
format!("{stdout}\n\n-------\n\n{stderr}")
};
if passed {
diag_group.passed.push(true);
diag_group.result = String::from("OK");
diag_group.logs.push(Log {
label: String::from("BCD"),
summary: vec![DVLine {
line_parts: vec![String::from("BCD: "), String::from("OK")],
line_colors: vec![Color::Reset, Color::Green],
}],
raw: output_text,
});
} else {
diag_group.passed.push(false);
diag_group.result = String::from("Unknown");
diag_group.logs.push(Log {
label: String::from("BCD"),
summary: vec![DVLine {
line_parts: vec![String::from("BCD: "), String::from("Unknown")],
line_colors: vec![Color::Reset, Color::Yellow],
}],
raw: output_text,
});
}
}
}
}
pub fn parse_bitlocker(diag_group: &mut DiagGroup, task_result: TaskResult) {
if !cfg!(windows) {
sleep(Duration::from_millis(500));
@ -151,7 +215,7 @@ pub fn parse_bitlocker(diag_group: &mut DiagGroup, task_result: TaskResult) {
let re_bitlocker_off = REGEXES.bitlocker_off();
match task_result {
TaskResult::Error(err) => {
diag_group.passed = false;
diag_group.passed.push(false);
diag_group.result = String::from("Error");
diag_group.logs.push(Log {
label: String::from("Bitlocker"),
@ -172,7 +236,7 @@ pub fn parse_bitlocker(diag_group: &mut DiagGroup, task_result: TaskResult) {
let set_result_text = !output_text.contains("All Key Protectors");
if re_bitlocker_off.is_match(&output_text) {
diag_group.passed &= true;
diag_group.passed.push(true);
result = String::from("OK");
diag_group.logs.push(Log {
label: String::from("Bitlocker"),
@ -183,7 +247,7 @@ pub fn parse_bitlocker(diag_group: &mut DiagGroup, task_result: TaskResult) {
raw: output_text,
});
} else if re_bitlocker_locked.is_match(&output_text) {
diag_group.passed = false;
diag_group.passed.push(false);
result = String::from("Locked");
diag_group.logs.push(Log {
label: String::from("Bitlocker"),
@ -194,7 +258,7 @@ pub fn parse_bitlocker(diag_group: &mut DiagGroup, task_result: TaskResult) {
raw: output_text,
});
} else {
diag_group.passed &= true;
diag_group.passed.push(true);
result = String::from("Unknown");
diag_group.logs.push(Log {
label: String::from("Bitlocker"),
@ -216,7 +280,7 @@ pub fn parse_chkdsk(diag_group: &mut DiagGroup, task_result: TaskResult) {
}
match task_result {
TaskResult::Error(err) => {
diag_group.passed = false;
diag_group.passed.push(false);
diag_group.result = String::from("Error");
diag_group.logs.push(Log {
label: String::from("CHKDSK"),
@ -241,7 +305,7 @@ pub fn parse_chkdsk(diag_group: &mut DiagGroup, task_result: TaskResult) {
if parsed_output.contains("Windows has scanned the file system and found no problems.")
{
diag_group.passed &= true;
diag_group.passed.push(true);
diag_group.result = String::from("OK");
diag_group.logs.push(Log {
label: String::from("CHKDSK"),
@ -252,7 +316,7 @@ pub fn parse_chkdsk(diag_group: &mut DiagGroup, task_result: TaskResult) {
raw: parsed_output,
});
} else {
diag_group.passed = false;
diag_group.passed.push(false);
diag_group.result = String::from("Error");
diag_group.logs.push(Log {
label: String::from("CHKDSK"),
@ -267,6 +331,110 @@ pub fn parse_chkdsk(diag_group: &mut DiagGroup, task_result: TaskResult) {
}
}
pub fn parse_dism(diag_group: &mut DiagGroup, task_result: TaskResult) {
if !cfg!(windows) {
sleep(Duration::from_millis(500));
return ();
}
match task_result {
TaskResult::Error(err) => {
diag_group.passed.push(false);
diag_group.result = String::from("Error");
diag_group.logs.push(Log {
label: String::from("DISM"),
summary: vec![DVLine {
line_parts: vec![String::from("DISM: "), 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}")
};
if output_text.contains("no component store corruption detected") {
diag_group.passed.push(true);
diag_group.result = String::from("OK");
diag_group.logs.push(Log {
label: String::from("DISM"),
summary: vec![DVLine {
line_parts: vec![String::from("DISM: "), String::from("OK")],
line_colors: vec![Color::Reset, Color::Green],
}],
raw: output_text,
});
} else {
diag_group.passed.push(false);
diag_group.result = String::from("Unknown");
diag_group.logs.push(Log {
label: String::from("DISM"),
summary: vec![DVLine {
line_parts: vec![String::from("DISM: "), String::from("Unknown")],
line_colors: vec![Color::Reset, Color::Yellow],
}],
raw: output_text,
});
}
}
}
}
pub fn parse_system_files(diag_group: &mut DiagGroup, task_result: TaskResult) {
if !cfg!(windows) {
sleep(Duration::from_millis(500));
return ();
}
match task_result {
TaskResult::Error(err) => {
diag_group.passed.push(false);
diag_group.result = String::from("Error");
diag_group.logs.push(Log {
label: String::from("System Files"),
summary: vec![DVLine {
line_parts: vec![String::from("System Files: "), String::from("Error")],
line_colors: vec![Color::Reset, Color::Red],
}],
raw: err,
});
}
TaskResult::Output(stdout, stderr, passed) => {
let output_text = if stderr.is_empty() {
stdout.clone()
} else {
format!("{stdout}\n\n{stderr}")
};
if passed {
diag_group.passed.push(true);
diag_group.result = String::from("OK");
diag_group.logs.push(Log {
label: String::from("System Files"),
summary: vec![DVLine {
line_parts: vec![String::from("System Files: "), String::from("OK")],
line_colors: vec![Color::Reset, Color::Green],
}],
raw: output_text,
});
} else {
diag_group.passed.push(false);
diag_group.result = String::from("Error");
diag_group.logs.push(Log {
label: String::from("System Files"),
summary: vec![DVLine {
line_parts: vec![String::from("System Files: "), String::from("Error")],
line_colors: vec![Color::Reset, Color::Red],
}],
raw: output_text,
});
}
}
}
}
pub fn remove_carriage_returns(text: &str) -> String {
let re_carriage_returns = REGEXES.carriage_returns();
let parsed_lines: Vec<String> = text