diff --git a/Cargo.lock b/Cargo.lock index 14defc5..4570983 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -184,6 +184,26 @@ dependencies = [ "generic-array", ] +[[package]] +name = "boot-diags" +version = "0.2.0" +dependencies = [ + "anyhow", + "clap", + "color-eyre", + "core", + "crossterm", + "futures", + "ratatui", + "serde", + "tokio", + "toml", + "tracing", + "tracing-error", + "tracing-subscriber", + "vergen-gix", +] + [[package]] name = "bstr" version = "1.11.3" diff --git a/Cargo.toml b/Cargo.toml index 39fe348..f5655e7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,6 +14,6 @@ # along with Deja-vu. If not, see . [workspace] -members = ["core", "deja_vu", "pe_menu"] -default-members = ["deja_vu", "pe_menu"] +members = ["core", "boot_diags", "deja_vu", "pe_menu"] +default-members = ["boot_diags", "deja_vu", "pe_menu"] resolver = "2" diff --git a/boot_diags/Cargo.toml b/boot_diags/Cargo.toml new file mode 100644 index 0000000..11a2402 --- /dev/null +++ b/boot_diags/Cargo.toml @@ -0,0 +1,46 @@ +# 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 . + +[package] +name = "boot-diags" +authors = ["2Shirt <2xShirt@gmail.com>"] +edition = "2021" +license = "GPL" +version = "0.2.0" + +[dependencies] +core = { path = "../core" } +clap = { version = "4.4.5", features = [ + "derive", + "cargo", + "wrap_help", + "unicode", + "string", + "unstable-styles", + ] } +color-eyre = "0.6.3" +crossterm = { version = "0.28.1", features = ["event-stream"] } +futures = "0.3.30" +ratatui = "0.29.0" +serde = { version = "1.0.217", features = ["derive"] } +tokio = { version = "1.43.0", features = ["full"] } +toml = "0.8.13" +tracing = "0.1.41" +tracing-error = "0.2.0" +tracing-subscriber = { version = "0.3.18", features = ["env-filter", "serde"] } + +[build-dependencies] +anyhow = "1.0.86" +vergen-gix = { version = "1.0.0", features = ["build", "cargo"] } diff --git a/boot_diags/build.rs b/boot_diags/build.rs new file mode 100644 index 0000000..c3ced6b --- /dev/null +++ b/boot_diags/build.rs @@ -0,0 +1,28 @@ +// 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 . +// +use anyhow::Result; +use vergen_gix::{BuildBuilder, CargoBuilder, Emitter, GixBuilder}; + +fn main() -> Result<()> { + let build = BuildBuilder::all_build()?; + let gix = GixBuilder::all_git()?; + let cargo = CargoBuilder::all_cargo()?; + Emitter::default() + .add_instructions(&build)? + .add_instructions(&gix)? + .add_instructions(&cargo)? + .emit() +} diff --git a/boot_diags/src/app.rs b/boot_diags/src/app.rs new file mode 100644 index 0000000..4f47083 --- /dev/null +++ b/boot_diags/src/app.rs @@ -0,0 +1,579 @@ +// 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 . +// +use core::{ + action::Action, + components::{ + footer::Footer, fps::FpsCounter, left::Left, popup, right::Right, state::StatefulList, + title::Title, Component, + }, + config::Config, + line::{get_disk_description_right, get_part_description, DVLine}, + state::{CloneSettings, Mode}, + system::{cpu::get_cpu_name, drivers}, + tasks::{Task, Tasks}, + tui::{Event, Tui}, +}; +use std::{ + iter::zip, + sync::{Arc, Mutex}, +}; + +use color_eyre::Result; +use ratatui::{ + crossterm::event::KeyEvent, + layout::{Constraint, Direction, Layout}, + prelude::Rect, + style::Color, +}; +use tokio::sync::mpsc; +use tracing::{debug, info}; + +pub struct App { + // TUI + action_rx: mpsc::UnboundedReceiver, + action_tx: mpsc::UnboundedSender, + components: Vec>, + config: Config, + frame_rate: f64, + last_tick_key_events: Vec, + should_quit: bool, + should_suspend: bool, + tick_rate: f64, + // App + clone: CloneSettings, + cur_mode: Mode, + list: StatefulList, + selections: Vec>, + tasks: Tasks, +} + +impl App { + pub fn new(tick_rate: f64, frame_rate: f64) -> Result { + let (action_tx, action_rx) = mpsc::unbounded_channel(); + let disk_list_arc = Arc::new(Mutex::new(Vec::new())); + let tasks = Tasks::new(action_tx.clone(), disk_list_arc.clone()); + let mut list = StatefulList::default(); + list.set_items(vec![ + Mode::BootDiags, + Mode::BootSetup, + Mode::InjectDrivers, + Mode::ToggleSafeMode, + ]); + Ok(Self { + // TUI + action_rx, + action_tx, + components: vec![ + Box::new(Title::new("Boot Diagnostics")), + Box::new(FpsCounter::new()), + Box::new(Left::new()), + Box::new(Right::new()), + Box::new(Footer::new()), + Box::new(popup::Popup::new()), + ], + config: Config::new()?, + frame_rate, + last_tick_key_events: Vec::new(), + should_quit: false, + should_suspend: false, + tick_rate, + // App + clone: CloneSettings::new(disk_list_arc), + cur_mode: Mode::Home, + list, + selections: vec![None, None], + tasks, + }) + } + + pub fn next_mode(&mut self) -> Mode { + match self.cur_mode { + Mode::Home => Mode::ScanDisks, + Mode::InstallDrivers => Mode::ScanDisks, + Mode::ScanDisks => Mode::SelectDisks, + Mode::SelectDisks => Mode::SelectParts, + Mode::SelectParts => Mode::DiagMenu, + Mode::DiagMenu => { + // Use highlighted entry + if let Some(new_mode) = self.list.get_selected() { + new_mode + } else { + self.cur_mode + } + } + Mode::Failed => Mode::Failed, + // Default to current mode + _ => self.cur_mode, + } + } + + pub fn set_mode(&mut self, new_mode: Mode) -> Result<()> { + info!("Setting mode to {new_mode:?}"); + self.cur_mode = new_mode; + match new_mode { + Mode::InjectDrivers | Mode::InstallDrivers => self.clone.scan_drivers(), + Mode::ScanDisks => { + if self.tasks.idle() { + self.tasks.add(Task::ScanDisks); + } + self.action_tx.send(Action::DisplayPopup( + popup::Type::Info, + String::from("Scanning Disks..."), + ))?; + } + _ => {} + } + Ok(()) + } + + pub async fn run(&mut self) -> Result<()> { + let mut tui = Tui::new()? + // .mouse(true) // uncomment this line to enable mouse support + .tick_rate(self.tick_rate) + .frame_rate(self.frame_rate); + tui.enter()?; + + for component in &mut self.components { + component.register_action_handler(self.action_tx.clone())?; + } + for component in &mut self.components { + component.register_config_handler(self.config.clone())?; + } + for component in &mut self.components { + component.init(tui.size()?)?; + } + + let action_tx = self.action_tx.clone(); + action_tx.send(Action::SetMode(Mode::ScanDisks))?; + loop { + self.handle_events(&mut tui).await?; + self.handle_actions(&mut tui)?; + if self.should_suspend { + tui.suspend()?; + action_tx.send(Action::Resume)?; + action_tx.send(Action::ClearScreen)?; + // tui.mouse(true); + tui.enter()?; + } else if self.should_quit { + tui.stop()?; + break; + } + } + tui.exit()?; + Ok(()) + } + + async fn handle_events(&mut self, tui: &mut Tui) -> Result<()> { + let Some(event) = tui.next_event().await else { + return Ok(()); + }; + let action_tx = self.action_tx.clone(); + match event { + Event::Quit => action_tx.send(Action::Quit)?, + Event::Tick => action_tx.send(Action::Tick)?, + Event::Render => action_tx.send(Action::Render)?, + Event::Resize(x, y) => action_tx.send(Action::Resize(x, y))?, + Event::Key(key) => self.handle_key_event(key)?, + _ => {} + } + for component in &mut self.components { + if let Some(action) = component.handle_events(Some(event.clone()))? { + action_tx.send(action)?; + } + } + Ok(()) + } + + fn handle_key_event(&mut self, key: KeyEvent) -> Result<()> { + let action_tx = self.action_tx.clone(); + let Some(keymap) = self.config.keybindings.get(&self.cur_mode) else { + return Ok(()); + }; + if let Some(action) = keymap.get(&vec![key]) { + info!("Got action: {action:?}"); + action_tx.send(action.clone())?; + } else { + // If the key was not handled as a single key action, + // then consider it for multi-key combinations. + self.last_tick_key_events.push(key); + + // Check for multi-key combinations + if let Some(action) = keymap.get(&self.last_tick_key_events) { + info!("Got action: {action:?}"); + action_tx.send(action.clone())?; + } + } + Ok(()) + } + + fn handle_actions(&mut self, tui: &mut Tui) -> Result<()> { + while let Ok(action) = self.action_rx.try_recv() { + if action != Action::Tick && action != Action::Render { + debug!("{action:?}"); + } + match action { + Action::Tick => { + self.last_tick_key_events.drain(..); + // Check background task(s) + match self.cur_mode { + Mode::ScanDisks => { + // Check background task + self.tasks.poll()?; // Once all are complete Action::NextScreen is sent + } + _ => {} + } + } + Action::Quit => self.should_quit = true, + Action::Suspend => self.should_suspend = true, + Action::Resume => self.should_suspend = false, + Action::ClearScreen => tui.terminal.clear()?, + Action::KeyUp => self.list.previous(), + Action::KeyDown => self.list.next(), + Action::Error(ref msg) => { + self.action_tx + .send(Action::DisplayPopup(popup::Type::Error, msg.clone()))?; + self.action_tx.send(Action::SetMode(Mode::Failed))?; + } + Action::Process => match self.cur_mode { + Mode::DiagMenu => { + let new_mode = self.next_mode(); + self.action_tx.send(Action::SetMode(new_mode))?; + } + _ => {} + }, + Action::Resize(w, h) => self.handle_resize(tui, w, h)?, + Action::Render => self.render(tui)?, + Action::ScanDisks => self.action_tx.send(Action::SetMode(Mode::ScanDisks))?, + Action::Select(one, two) => { + match self.cur_mode { + Mode::InstallDrivers => { + if let Some(index) = one { + if let Some(driver) = self.clone.driver_list.get(index).cloned() { + drivers::load(&driver.inf_paths); + self.clone.driver = Some(driver); + } + } + } + Mode::SelectDisks => { + self.clone.disk_index_dest = one; + } + Mode::SelectParts => { + self.clone.part_index_boot = one; + self.clone.part_index_os = two; + } + _ => {} + } + self.selections[0] = one; + self.selections[1] = two; + } + Action::SetMode(new_mode) => { + self.cur_mode = new_mode; + self.set_mode(new_mode)?; + self.action_tx + .send(Action::UpdateFooter(build_footer_string(self.cur_mode)))?; + self.action_tx.send(build_left_items(self))?; + self.action_tx.send(build_right_items(self))?; + self.action_tx.send(Action::Select(None, None))?; + } + _ => {} + } + for component in &mut self.components { + if let Some(action) = component.update(action.clone())? { + self.action_tx.send(action)?; + }; + } + } + Ok(()) + } + + fn handle_resize(&mut self, tui: &mut Tui, w: u16, h: u16) -> Result<()> { + tui.resize(Rect::new(0, 0, w, h))?; + self.render(tui)?; + Ok(()) + } + + fn render(&mut self, tui: &mut Tui) -> Result<()> { + tui.draw(|frame| { + if let [header, _body, footer, left, right, popup] = get_chunks(frame.area())[..] { + let component_areas = vec![ + header, // Title Bar + header, // FPS Counter + left, right, footer, popup, + ]; + for (component, area) in zip(self.components.iter_mut(), component_areas) { + if let Err(err) = component.draw(frame, area) { + let _ = self + .action_tx + .send(Action::Error(format!("Failed to draw: {err:?}"))); + } + } + }; + })?; + Ok(()) + } +} + +fn centered_rect(percent_x: u16, percent_y: u16, r: Rect) -> Rect { + // Cut the given rectangle into three vertical pieces + let popup_layout = Layout::default() + .direction(Direction::Vertical) + .constraints([ + Constraint::Percentage((100 - percent_y) / 2), + Constraint::Percentage(percent_y), + Constraint::Percentage((100 - percent_y) / 2), + ]) + .split(r); + + // Then cut the middle vertical piece into three width-wise pieces + Layout::default() + .direction(Direction::Horizontal) + .constraints([ + Constraint::Percentage((100 - percent_x) / 2), + Constraint::Percentage(percent_x), + Constraint::Percentage((100 - percent_x) / 2), + ]) + .split(popup_layout[1])[1] // Return the middle chunk +} + +fn get_chunks(r: Rect) -> Vec { + let mut chunks: Vec = Vec::with_capacity(6); + + // Main sections + chunks.extend( + Layout::default() + .direction(Direction::Vertical) + .constraints([ + Constraint::Length(3), + Constraint::Min(1), + Constraint::Length(3), + ]) + .split(r) + .to_vec(), + ); + + // Left/Right + chunks.extend( + Layout::default() + .direction(Direction::Horizontal) + .constraints([Constraint::Percentage(50), Constraint::Percentage(50)]) + .split(centered_rect(90, 90, chunks[1])) + .to_vec(), + ); + + // Popup + chunks.push(centered_rect(60, 25, r)); + + // Done + chunks +} + +fn build_footer_string(cur_mode: Mode) -> String { + match cur_mode { + // Boot Diags + Mode::BootSetup + | Mode::DiagMenu + | Mode::InjectDrivers + | Mode::PEMenu + | Mode::ToggleSafeMode => String::from("(Enter) or (q) to quit"), + // Copied from Clone + Mode::Home | Mode::ScanDisks | Mode::BootDiags => String::from("(q) to quit"), + Mode::SelectParts => String::from("(Enter) to select / (s) to start over / (q) to quit"), + Mode::SelectDisks => String::from( + "(Enter) to select / / (i) to install driver / (r) to rescan / (q) to quit", + ), + Mode::SelectTableType => String::from("(Enter) to select / (b) to go back / (q) to quit"), + Mode::Confirm => String::from("(Enter) to confirm / (b) to go back / (q) to quit"), + Mode::Done | Mode::Failed | Mode::InstallDrivers => String::from("(Enter) or (q) to quit"), + // Invalid states + Mode::Clone | Mode::PreClone | Mode::PostClone => panic!("This shouldn't happen?"), + } +} + +fn build_left_items(app: &App) -> Action { + let mut items = Vec::new(); + let mut labels = vec![String::new(), String::new()]; + let select_num: usize; + let title: String; + match app.cur_mode { + Mode::Home => { + select_num = 0; + title = String::from("Home"); + } + Mode::DiagMenu => { + select_num = 0; + title = String::from("Troubleshooting"); + app.list.items.iter().for_each(|mode| { + let (name, _) = get_mode_strings(*mode); + items.push(name); + }); + } + Mode::InstallDrivers => { + select_num = 1; + title = String::from("Install Drivers"); + app.clone + .driver_list + .iter() + .for_each(|driver| items.push(driver.to_string())); + } + Mode::InjectDrivers => { + select_num = 1; + title = String::from("Select Drivers"); + app.clone + .driver_list + .iter() + .for_each(|driver| items.push(driver.to_string())); + } + Mode::SelectDisks => { + select_num = 1; + title = String::from("Select Disk"); + let disk_list = app.clone.disk_list.lock().unwrap(); + disk_list + .iter() + .for_each(|disk| items.push(disk.description.to_string())); + } + Mode::ScanDisks => { + select_num = 0; + title = String::from("Processing"); + } + Mode::SelectParts => { + select_num = 2; + title = String::from("Select Boot and OS Partitions"); + labels.push(String::from("boot")); + labels.push(String::from("os")); + let disk_list = app.clone.disk_list.lock().unwrap(); + if let Some(index) = app.clone.disk_index_dest { + if let Some(disk) = disk_list.get(index) { + disk.get_parts().iter().for_each(|part| { + items.push(part.to_string()); + }); + } + } + } + Mode::BootDiags | Mode::BootSetup | Mode::ToggleSafeMode => { + select_num = 0; + let (new_title, _) = get_mode_strings(app.cur_mode); + title = new_title; + } + Mode::Done | Mode::Failed => { + select_num = 0; + title = String::from("Done"); + } + // Invalid states + Mode::SelectTableType + | Mode::PEMenu + | Mode::Confirm + | Mode::PreClone + | Mode::Clone + | Mode::PostClone => { + panic!("This shouldn't happen?") + } + }; + Action::UpdateLeft(title, labels, items, select_num) +} + +fn build_right_items(app: &App) -> Action { + let mut items = Vec::new(); + let mut labels: Vec> = Vec::new(); + let mut start_index = 0; + match app.cur_mode { + Mode::DiagMenu => { + app.list.items.iter().for_each(|mode| { + let (name, description) = get_mode_strings(*mode); + items.push(vec![ + DVLine { + line_parts: vec![name], + line_colors: vec![Color::Cyan], + }, + DVLine { + line_parts: vec![String::new()], + line_colors: vec![Color::Reset], + }, + DVLine { + line_parts: vec![description], + line_colors: vec![Color::Reset], + }, + ]); + }); + } + Mode::InjectDrivers | Mode::InstallDrivers => { + items.push(vec![DVLine { + line_parts: vec![String::from("CPU")], + line_colors: vec![Color::Cyan], + }]); + items.push(vec![DVLine { + line_parts: vec![get_cpu_name()], + line_colors: vec![Color::Reset], + }]); + start_index = 2; + } + Mode::SelectDisks => { + let dest_dv_line = DVLine { + line_parts: vec![String::from("Disk")], + line_colors: vec![Color::Cyan, Color::Red], + }; + labels.push(vec![dest_dv_line]); + let disk_list = app.clone.disk_list.lock().unwrap(); + disk_list + .iter() + .for_each(|disk| items.push(get_disk_description_right(&disk))); + } + Mode::SelectParts => { + vec!["Boot", "OS"].iter().for_each(|s| { + labels.push(vec![DVLine { + line_parts: vec![String::from(*s)], + line_colors: vec![Color::Cyan], + }]) + }); + if let Some(index) = app.clone.disk_index_dest { + start_index = 1; + let disk_list = app.clone.disk_list.lock().unwrap(); + if let Some(disk) = disk_list.get(index) { + // Disk Details + items.push(get_disk_description_right(&disk)); + + // Partition Details + disk.parts + .iter() + .for_each(|part| items.push(get_part_description(&part))); + } + } + } + _ => {} + } + Action::UpdateRight(labels, start_index, items) +} + +fn get_mode_strings(mode: Mode) -> (String, String) { + match mode { + Mode::BootDiags => ( + String::from("Boot Diagnostics"), + String::from("Check for common Windows boot issues"), + ), + Mode::BootSetup => ( + String::from("Boot Setup"), + String::from("Create or recreate boot files"), + ), + Mode::InjectDrivers => ( + String::from("Inject Drivers"), + String::from("Inject drivers into existing Windows environment"), + ), + Mode::ToggleSafeMode => ( + String::from("Toggle Safe Mode"), + String::from("Enable or disable safe mode"), + ), + _ => panic!("This shouldn't happen"), + } +} diff --git a/boot_diags/src/main.rs b/boot_diags/src/main.rs new file mode 100644 index 0000000..8bcfda9 --- /dev/null +++ b/boot_diags/src/main.rs @@ -0,0 +1,33 @@ +// 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 . +// +use clap::Parser; +use color_eyre::Result; +use core; + +use crate::app::App; + +mod app; + +#[tokio::main] +async fn main() -> Result<()> { + core::errors::init()?; + core::logging::init()?; + + let args = core::cli::Cli::parse(); + let mut app = App::new(args.tick_rate, args.frame_rate)?; + app.run().await?; + Ok(()) +} diff --git a/config/config.json5 b/config/config.json5 index 986126d..8fb4fbb 100644 --- a/config/config.json5 +++ b/config/config.json5 @@ -95,6 +95,51 @@ "": "Quit", "": "Suspend" }, + "DiagMenu": { + "": "Process", + "": "KeyUp", + "": "KeyDown", + "": "Quit", + "": "Quit", + "": "Quit", + "": "Suspend" + }, + "BootDiags": { + "": "Process", + "": "KeyUp", + "": "KeyDown", + "": "Quit", + "": "Quit", + "": "Quit", + "": "Suspend" + }, + "BootSetup": { + "": "Process", + "": "KeyUp", + "": "KeyDown", + "": "Quit", + "": "Quit", + "": "Quit", + "": "Suspend" + }, + "InjectDrivers": { + "": "Process", + "": "KeyUp", + "": "KeyDown", + "": "Quit", + "": "Quit", + "": "Quit", + "": "Suspend" + }, + "ToggleSafeMode": { + "": "Process", + "": "KeyUp", + "": "KeyDown", + "": "Quit", + "": "Quit", + "": "Quit", + "": "Suspend" + }, "PEMenu": { "": "Process", "": "KeyUp", diff --git a/core/src/action.rs b/core/src/action.rs index 604edd7..c4f6a4f 100644 --- a/core/src/action.rs +++ b/core/src/action.rs @@ -20,6 +20,8 @@ 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? // App (Clone) Highlight(usize), InstallDriver, @@ -35,7 +37,7 @@ pub enum Action { // 1: For a single choice // 2: For two selections (obviously) UpdateRight(Vec>, usize, Vec>), // (labels, start_index, items) - items before start are always shown - // App (PEMenu) + // App (PE-Menu) OpenTerminal, // Screens DismissPopup, diff --git a/core/src/state.rs b/core/src/state.rs index fab9ede..1794229 100644 --- a/core/src/state.rs +++ b/core/src/state.rs @@ -30,6 +30,12 @@ pub enum Mode { Home, Done, Failed, + // Boot Diags + DiagMenu, + BootDiags, + BootSetup, + InjectDrivers, + ToggleSafeMode, // Clone ScanDisks, InstallDrivers, diff --git a/deja_vu/src/app.rs b/deja_vu/src/app.rs index ae6ef72..40e998a 100644 --- a/deja_vu/src/app.rs +++ b/deja_vu/src/app.rs @@ -115,7 +115,12 @@ impl App { | Mode::Clone | Mode::PostClone => None, // Invalid states - Mode::PEMenu => panic!("This shouldn't happen?"), + Mode::BootDiags + | Mode::BootSetup + | Mode::DiagMenu + | Mode::InjectDrivers + | Mode::PEMenu + | Mode::ToggleSafeMode => panic!("This shouldn't happen?"), }; new_mode } @@ -134,7 +139,12 @@ impl App { Mode::PostClone | Mode::Done => Mode::Done, Mode::Failed => Mode::Failed, // Invalid states - Mode::PEMenu => panic!("This shouldn't happen?"), + Mode::BootDiags + | Mode::BootSetup + | Mode::DiagMenu + | Mode::InjectDrivers + | Mode::PEMenu + | Mode::ToggleSafeMode => panic!("This shouldn't happen?"), }; if new_mode == self.cur_mode { @@ -573,7 +583,12 @@ fn build_footer_string(cur_mode: Mode) -> String { Mode::Confirm => String::from("(Enter) to confirm / (b) to go back / (q) to quit"), Mode::Done | Mode::Failed | Mode::InstallDrivers => String::from("(Enter) or (q) to quit"), // Invalid states - Mode::PEMenu => panic!("This shouldn't happen?"), + Mode::BootDiags + | Mode::BootSetup + | Mode::DiagMenu + | Mode::InjectDrivers + | Mode::PEMenu + | Mode::ToggleSafeMode => panic!("This shouldn't happen?"), } } @@ -638,7 +653,12 @@ fn build_left_items(app: &App, cur_mode: Mode) -> Action { title = String::from("Done"); } // Invalid states - Mode::PEMenu => panic!("This shouldn't happen?"), + Mode::BootDiags + | Mode::BootSetup + | Mode::DiagMenu + | Mode::InjectDrivers + | Mode::PEMenu + | Mode::ToggleSafeMode => panic!("This shouldn't happen?"), }; Action::UpdateLeft(title, labels, items, select_num) }