Use separate State structs per app

This commit is contained in:
2Shirt 2025-11-08 16:17:34 -08:00
parent ee7de8f355
commit 7e12223344
Signed by: 2Shirt
GPG key ID: 152FAC923B0E132C
8 changed files with 166 additions and 104 deletions

View file

@ -26,7 +26,7 @@ use core::{
},
config::Config,
line::{DVLine, get_disk_description_right, get_part_description},
state::{CloneSettings, Mode},
state::Mode,
system::{
boot::{self, SafeMode, configure_disk},
cpu::get_cpu_name,
@ -57,6 +57,7 @@ use crate::{
parse_dism, parse_registry_hives, parse_system_files,
},
scan,
state::State,
};
pub struct App {
@ -71,13 +72,13 @@ pub struct App {
should_suspend: bool,
tick_rate: f64,
// App
clone: CloneSettings,
cur_mode: Mode,
diag_groups: Arc<Mutex<Vec<DiagGroup>>>,
list: StatefulList<Mode>,
boot_modes: Vec<SafeMode>,
setup_modes: Vec<SafeMode>,
selections: Vec<Option<usize>>,
state: State,
system32: String,
tasks: Tasks,
}
@ -117,7 +118,7 @@ impl App {
should_suspend: false,
tick_rate,
// App
clone: CloneSettings::new(disk_list_arc),
state: State::new(disk_list_arc),
cur_mode: Mode::Home,
diag_groups: diag_groups_arc,
list,
@ -130,12 +131,12 @@ impl App {
}
pub fn inject_driver(&mut self, index: usize) {
if let Some(driver) = self.clone.driver_list.get(index)
&& let Some(disk_index) = self.clone.disk_index_dest
if let Some(driver) = self.state.driver_list.get(index)
&& let Some(disk_index) = self.state.disk_index_dest
{
let disk_list = self.clone.disk_list.lock().unwrap();
let disk_list = self.state.disk_list.lock().unwrap();
if let Some(disk) = disk_list.get(disk_index)
&& let Some(os_index) = self.clone.part_index_os
&& let Some(os_index) = self.state.part_index_os
&& let Ok(task) = boot::inject_driver(
driver,
disk.get_part_letter(os_index).as_str(),
@ -171,10 +172,10 @@ impl App {
SafeMode::Enable => "Safe Mode (minimal)",
};
info!("Setting boot mode to: {new_mode}");
let disk_list = self.clone.disk_list.lock().unwrap();
if let Some(disk_index) = self.clone.disk_index_dest
let disk_list = self.state.disk_list.lock().unwrap();
if let Some(disk_index) = self.state.disk_index_dest
&& let Some(disk) = disk_list.get(disk_index)
&& let Some(boot_index) = self.clone.part_index_boot
&& let Some(boot_index) = self.state.part_index_boot
&& let Ok(task) = boot::set_mode(
disk.get_part_letter(boot_index).as_str(),
&boot_mode,
@ -202,13 +203,13 @@ impl App {
))?;
scan::queue_boot_scan_tasks(
self.action_tx.clone(),
&self.clone,
self.diag_groups.clone(),
&self.state,
self.system32.clone(),
&mut self.tasks,
)?;
}
Mode::InjectDrivers | Mode::InstallDrivers => self.clone.scan_drivers(),
Mode::InjectDrivers | Mode::InstallDrivers => self.state.scan_drivers(),
Mode::Process => {
self.action_tx
.send(Action::DisplayPopup(popup::Type::Info, String::from("...")))?;
@ -383,10 +384,10 @@ impl App {
}
Mode::InstallDrivers => {
if let Some(index) = one
&& let Some(driver) = self.clone.driver_list.get(index).cloned()
&& let Some(driver) = self.state.driver_list.get(index).cloned()
{
drivers::load(&driver.inf_paths);
self.clone.driver = Some(driver);
self.state.driver = Some(driver);
}
}
Mode::BootSetup => {
@ -398,11 +399,11 @@ impl App {
}
}
Mode::SelectDisks => {
self.clone.disk_index_dest = one;
self.state.disk_index_dest = one;
}
Mode::SelectParts => {
self.clone.part_index_boot = one;
self.clone.part_index_os = two;
self.state.part_index_boot = one;
self.state.part_index_os = two;
}
Mode::SetBootMode => {
if let Some(index) = one
@ -683,7 +684,7 @@ fn build_left_items(app: &App) -> Action {
Mode::InstallDrivers => {
select_type = SelectionType::One;
title = String::from("Install Drivers");
app.clone
app.state
.driver_list
.iter()
.for_each(|driver| items.push(driver.to_string()));
@ -691,7 +692,7 @@ fn build_left_items(app: &App) -> Action {
Mode::InjectDrivers => {
select_type = SelectionType::One;
title = String::from("Select Drivers");
app.clone
app.state
.driver_list
.iter()
.for_each(|driver| items.push(driver.to_string()));
@ -699,7 +700,7 @@ fn build_left_items(app: &App) -> Action {
Mode::SelectDisks => {
select_type = SelectionType::One;
title = String::from("Select Disk");
let disk_list = app.clone.disk_list.lock().unwrap();
let disk_list = app.state.disk_list.lock().unwrap();
disk_list
.iter()
.for_each(|disk| items.push(disk.description.to_string()));
@ -713,8 +714,8 @@ fn build_left_items(app: &App) -> Action {
title = String::from("Select Boot and OS Partitions");
labels[0] = String::from("boot");
labels[1] = String::from("os");
let disk_list = app.clone.disk_list.lock().unwrap();
if let Some(index) = app.clone.disk_index_dest
let disk_list = app.state.disk_list.lock().unwrap();
if let Some(index) = app.state.disk_index_dest
&& let Some(disk) = disk_list.get(index)
{
disk.get_parts().iter().for_each(|part| {
@ -854,7 +855,7 @@ fn build_right_items(app: &App) -> Action {
line_colors: vec![Color::Cyan],
};
labels.push(vec![dest_dv_line]);
let disk_list = app.clone.disk_list.lock().unwrap();
let disk_list = app.state.disk_list.lock().unwrap();
disk_list
.iter()
.for_each(|disk| items.push(get_disk_description_right(disk, &None)));
@ -866,9 +867,9 @@ fn build_right_items(app: &App) -> Action {
line_colors: vec![Color::Cyan],
}])
});
if let Some(index) = app.clone.disk_index_dest {
if let Some(index) = app.state.disk_index_dest {
start_index += 1;
let disk_list = app.clone.disk_list.lock().unwrap();
let disk_list = app.state.disk_list.lock().unwrap();
if let Some(disk) = disk_list.get(index) {
// Disk Details
items.push(get_disk_description_right(disk, &None));
@ -897,11 +898,11 @@ fn build_right_items(app: &App) -> Action {
fn create_boot_files(app: &mut App, safe_mode: SafeMode) {
let mut tasks: Vec<TaskType> = Vec::new();
if let Some(index) = app.clone.disk_index_dest {
let disk_list = app.clone.disk_list.lock().unwrap();
if let Some(index) = app.state.disk_index_dest {
let disk_list = app.state.disk_list.lock().unwrap();
if let Some(disk) = disk_list.get(index) {
let letter_boot = disk.get_part_letter(app.clone.part_index_boot.unwrap());
let letter_os = disk.get_part_letter(app.clone.part_index_os.unwrap());
let letter_boot = disk.get_part_letter(app.state.part_index_boot.unwrap());
let letter_os = disk.get_part_letter(app.state.part_index_os.unwrap());
tasks = configure_disk(
&letter_boot,
&letter_os,
@ -918,14 +919,14 @@ fn create_boot_files(app: &mut App, safe_mode: SafeMode) {
fn get_disk_header(app: &App) -> Vec<DVLine> {
let mut header_lines: Vec<DVLine> = Vec::new();
if let Some(index) = app.clone.disk_index_dest {
let disk_list = app.clone.disk_list.lock().unwrap();
if let Some(index) = app.state.disk_index_dest {
let disk_list = app.state.disk_list.lock().unwrap();
if let Some(disk) = disk_list.get(index) {
let mut parts: Vec<usize> = Vec::new();
if let Some(index) = app.clone.part_index_boot {
if let Some(index) = app.state.part_index_boot {
parts.push(index);
}
if let Some(index) = app.clone.part_index_os {
if let Some(index) = app.state.part_index_os {
parts.push(index);
}
header_lines.append(&mut get_disk_description_right(disk, &Some(parts)));

View file

@ -22,6 +22,7 @@ mod app;
mod components;
mod diags;
mod scan;
mod state;
#[tokio::main]
async fn main() -> Result<()> {

View file

@ -1,5 +1,4 @@
use color_eyre::Result;
use core::state::CloneSettings;
use core::system::disk::PartitionTableType;
use core::tasks::Tasks;
use core::{action::Action, tasks::TaskType};
@ -8,24 +7,25 @@ use std::sync::{Arc, Mutex};
use tokio::sync::mpsc;
use crate::diags::{DiagGroup, Type as DiagType};
use crate::state::State;
pub fn queue_boot_scan_tasks(
action_tx: mpsc::UnboundedSender<Action>,
clone: &CloneSettings,
diag_groups: Arc<Mutex<Vec<DiagGroup>>>,
state: &State,
system32: String,
tasks: &mut Tasks,
) -> Result<()> {
if let Ok(mut diag_groups) = diag_groups.lock() {
diag_groups.clear();
}
let disk_list = clone.disk_list.lock().unwrap();
if let Some(disk_index) = clone.disk_index_dest
let disk_list = state.disk_list.lock().unwrap();
if let Some(disk_index) = state.disk_index_dest
&& let Some(disk) = disk_list.get(disk_index)
{
let table_type = disk.part_type.clone();
let letter_boot = disk.get_part_letter(clone.part_index_boot.unwrap());
let letter_os = disk.get_part_letter(clone.part_index_os.unwrap());
let letter_boot = disk.get_part_letter(state.part_index_boot.unwrap());
let letter_os = disk.get_part_letter(state.part_index_os.unwrap());
// Safety check
if letter_os.is_empty() {

42
boot_diags/src/state.rs Normal file
View file

@ -0,0 +1,42 @@
// 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 <https://www.gnu.org/licenses/>.
//
use std::sync::{Arc, Mutex};
use core::system::{disk::Disk, drivers};
#[derive(Debug, Default)]
pub struct State {
pub disk_index_dest: Option<usize>,
pub disk_list: Arc<Mutex<Vec<Disk>>>,
pub driver_list: Vec<drivers::Driver>,
pub part_index_boot: Option<usize>,
pub part_index_os: Option<usize>,
pub driver: Option<drivers::Driver>,
}
impl State {
pub fn new(disk_list: Arc<Mutex<Vec<Disk>>>) -> Self {
State {
disk_list,
..Default::default()
}
}
pub fn scan_drivers(&mut self) {
self.driver_list = drivers::scan();
}
}

View file

@ -14,15 +14,8 @@
// along with Deja-Vu. If not, see <https://www.gnu.org/licenses/>.
//
use std::sync::{Arc, Mutex};
use serde::{Deserialize, Serialize};
use crate::system::{
disk::{Disk, PartitionTableType},
drivers,
};
#[derive(Default, Debug, Copy, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum Mode {
// Core
@ -52,28 +45,3 @@ pub enum Mode {
// WinPE
PEMenu,
}
#[derive(Debug, Default)]
pub struct CloneSettings {
pub disk_index_dest: Option<usize>,
pub disk_index_source: Option<usize>,
pub disk_list: Arc<Mutex<Vec<Disk>>>,
pub driver_list: Vec<drivers::Driver>,
pub part_index_boot: Option<usize>,
pub part_index_os: Option<usize>,
pub driver: Option<drivers::Driver>,
pub table_type: Option<PartitionTableType>,
}
impl CloneSettings {
pub fn new(disk_list: Arc<Mutex<Vec<Disk>>>) -> Self {
CloneSettings {
disk_list,
..Default::default()
}
}
pub fn scan_drivers(&mut self) {
self.driver_list = drivers::scan();
}
}

View file

@ -26,7 +26,7 @@ use core::{
},
config::Config,
line::{DVLine, get_disk_description_right, get_part_description},
state::{CloneSettings, Mode},
state::Mode,
system::{
boot, cpu::get_cpu_name, disk::PartitionTableType, diskpart::build_dest_format_script,
drivers,
@ -51,6 +51,8 @@ use ratatui::{
use tokio::sync::mpsc;
use tracing::{debug, info};
use crate::state::State;
pub struct App {
// TUI
action_rx: mpsc::UnboundedReceiver<Action>,
@ -63,11 +65,11 @@ pub struct App {
should_suspend: bool,
tick_rate: f64,
// App
clone: CloneSettings,
cur_mode: Mode,
list: StatefulList<usize>,
prev_mode: Mode,
selections: Vec<Option<usize>>,
state: State,
tasks: Tasks,
}
@ -94,7 +96,7 @@ impl App {
should_suspend: false,
tick_rate,
// App
clone: CloneSettings::new(disk_list_arc),
state: State::new(disk_list_arc),
cur_mode: Mode::default(),
list: StatefulList::default(),
prev_mode: Mode::default(),
@ -167,7 +169,7 @@ impl App {
info!("Setting mode to {new_mode:?}");
self.cur_mode = new_mode;
match new_mode {
Mode::InstallDrivers => self.clone.scan_drivers(),
Mode::InstallDrivers => self.state.scan_drivers(),
Mode::ScanDisks => {
self.prev_mode = self.cur_mode;
if self.tasks.idle() {
@ -194,11 +196,11 @@ impl App {
));
// Build Diskpart script to format destination disk
let disk_list = self.clone.disk_list.lock().unwrap();
if let Some(disk_index) = self.clone.disk_index_dest
let disk_list = self.state.disk_list.lock().unwrap();
if let Some(disk_index) = self.state.disk_index_dest
&& let Some(disk) = disk_list.get(disk_index)
{
let table_type = self.clone.table_type.clone().unwrap();
let table_type = self.state.table_type.clone().unwrap();
let diskpart_script = build_dest_format_script(disk.id, &table_type);
self.tasks.add(TaskType::Diskpart(diskpart_script));
}
@ -212,7 +214,7 @@ impl App {
self.config.clone_app_path.clone(),
Vec::new(),
));
if let Some(dest_index) = self.clone.disk_index_dest {
if let Some(dest_index) = self.state.disk_index_dest {
self.tasks.add(TaskType::UpdateDestDisk(dest_index));
}
}
@ -226,13 +228,13 @@ impl App {
let system32 = get_system32_path(&self.action_tx);
// Add actions
let disk_list = self.clone.disk_list.lock().unwrap();
if let Some(disk_index) = self.clone.disk_index_dest
let disk_list = self.state.disk_list.lock().unwrap();
if let Some(disk_index) = self.state.disk_index_dest
&& let Some(disk) = disk_list.get(disk_index)
{
let table_type = self.clone.table_type.clone().unwrap();
let letter_boot = disk.get_part_letter(self.clone.part_index_boot.unwrap());
let letter_os = disk.get_part_letter(self.clone.part_index_os.unwrap());
let table_type = self.state.table_type.clone().unwrap();
let letter_boot = disk.get_part_letter(self.state.part_index_boot.unwrap());
let letter_os = disk.get_part_letter(self.state.part_index_os.unwrap());
// Safety check
if letter_boot.is_empty() || letter_os.is_empty() {
@ -254,7 +256,7 @@ impl App {
}
// Inject driver(s) (if selected)
if let Some(driver) = &self.clone.driver {
if let Some(driver) = &self.state.driver {
if let Ok(task) = boot::inject_driver(driver, &letter_os, &system32) {
self.tasks.add(task);
} else {
@ -414,22 +416,22 @@ impl App {
match self.cur_mode {
Mode::InstallDrivers => {
if let Some(index) = one
&& let Some(driver) = self.clone.driver_list.get(index).cloned()
&& let Some(driver) = self.state.driver_list.get(index).cloned()
{
drivers::load(&driver.inf_paths);
self.clone.driver = Some(driver);
self.state.driver = Some(driver);
}
}
Mode::SelectDisks => {
self.clone.disk_index_source = one;
self.clone.disk_index_dest = two;
self.state.disk_index_source = one;
self.state.disk_index_dest = two;
}
Mode::SelectParts => {
self.clone.part_index_boot = one;
self.clone.part_index_os = two;
self.state.part_index_boot = one;
self.state.part_index_os = two;
}
Mode::SelectTableType => {
self.clone.table_type = {
self.state.table_type = {
if let Some(index) = one {
match index {
0 => Some(PartitionTableType::Guid),
@ -452,7 +454,7 @@ impl App {
// Clear TableType selection
match new_mode {
Mode::SelectDisks | Mode::SelectTableType => {
self.clone.table_type = None;
self.state.table_type = None;
}
_ => {}
}
@ -466,8 +468,8 @@ impl App {
Mode::SelectTableType | Mode::Confirm => {
// Select source/dest disks
self.action_tx.send(Action::SelectRight(
self.clone.disk_index_source,
self.clone.disk_index_dest,
self.state.disk_index_source,
self.state.disk_index_dest,
))?;
}
Mode::SelectParts => {
@ -475,7 +477,7 @@ impl App {
self.action_tx.send(Action::Select(Some(0), None))?;
// Highlight 2nd or 3rd partition as OS partition
let index = if let Some(table_type) = &self.clone.table_type {
let index = if let Some(table_type) = &self.state.table_type {
match table_type {
PartitionTableType::Guid => 2,
PartitionTableType::Legacy => 1,
@ -649,7 +651,7 @@ fn build_left_items(app: &App, cur_mode: Mode) -> Action {
Mode::InstallDrivers => {
select_type = SelectionType::One;
title = String::from("Install Drivers");
app.clone
app.state
.driver_list
.iter()
.for_each(|driver| items.push(driver.to_string()));
@ -659,7 +661,7 @@ fn build_left_items(app: &App, cur_mode: Mode) -> Action {
title = String::from("Select Source and Destination Disks");
labels.push(String::from("source"));
labels.push(String::from("dest"));
let disk_list = app.clone.disk_list.lock().unwrap();
let disk_list = app.state.disk_list.lock().unwrap();
disk_list
.iter()
.for_each(|disk| items.push(disk.description.to_string()));
@ -683,8 +685,8 @@ fn build_left_items(app: &App, cur_mode: Mode) -> Action {
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
let disk_list = app.state.disk_list.lock().unwrap();
if let Some(index) = app.state.disk_index_dest
&& let Some(disk) = disk_list.get(index)
{
disk.get_parts().iter().for_each(|part| {
@ -739,7 +741,7 @@ fn build_right_items(app: &App, cur_mode: Mode) -> Action {
],
line_colors: vec![Color::Cyan, Color::Red],
};
if let Some(table_type) = &app.clone.table_type {
if let Some(table_type) = &app.state.table_type {
// Show table type
let type_str = match table_type {
PartitionTableType::Guid => "GPT",
@ -755,7 +757,7 @@ fn build_right_items(app: &App, cur_mode: Mode) -> Action {
} else {
labels.push(vec![dest_dv_line]);
}
let disk_list = app.clone.disk_list.lock().unwrap();
let disk_list = app.state.disk_list.lock().unwrap();
disk_list
.iter()
.for_each(|disk| items.push(get_disk_description_right(disk, &None)));
@ -767,9 +769,9 @@ fn build_right_items(app: &App, cur_mode: Mode) -> Action {
line_colors: vec![Color::Cyan],
}]);
}
if let Some(index) = app.clone.disk_index_dest {
if let Some(index) = app.state.disk_index_dest {
start_index = 1;
let disk_list = app.clone.disk_list.lock().unwrap();
let disk_list = app.state.disk_list.lock().unwrap();
if let Some(disk) = disk_list.get(index) {
// Disk Details
items.push(get_disk_description_right(disk, &None));

View file

@ -19,6 +19,7 @@ use color_eyre::Result;
use crate::app::App;
mod app;
mod state;
#[tokio::main]
async fn main() -> Result<()> {

47
deja_vu/src/state.rs Normal file
View file

@ -0,0 +1,47 @@
// 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 <https://www.gnu.org/licenses/>.
//
use std::sync::{Arc, Mutex};
use core::system::{
disk::{Disk, PartitionTableType},
drivers,
};
#[derive(Debug, Default)]
pub struct State {
pub disk_index_dest: Option<usize>,
pub disk_index_source: Option<usize>,
pub disk_list: Arc<Mutex<Vec<Disk>>>,
pub driver_list: Vec<drivers::Driver>,
pub part_index_boot: Option<usize>,
pub part_index_os: Option<usize>,
pub driver: Option<drivers::Driver>,
pub table_type: Option<PartitionTableType>,
}
impl State {
pub fn new(disk_list: Arc<Mutex<Vec<Disk>>>) -> Self {
State {
disk_list,
..Default::default()
}
}
pub fn scan_drivers(&mut self) {
self.driver_list = drivers::scan();
}
}