Compare commits
5 commits
81aa7a8984
...
9d6fa954b2
| Author | SHA1 | Date | |
|---|---|---|---|
| 9d6fa954b2 | |||
| 253385c2a7 | |||
| fc0b82418b | |||
| 608f07d445 | |||
| 828a2736be |
9 changed files with 108 additions and 69 deletions
|
|
@ -17,8 +17,8 @@ use serde::{Deserialize, Serialize};
|
||||||
use strum::Display;
|
use strum::Display;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
app::Mode,
|
|
||||||
components::popup::Type,
|
components::popup::Type,
|
||||||
|
state::Mode,
|
||||||
system::{
|
system::{
|
||||||
disk::{Disk, PartitionTableType},
|
disk::{Disk, PartitionTableType},
|
||||||
drivers::Driver,
|
drivers::Driver,
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,6 @@ use ratatui::{
|
||||||
layout::{Constraint, Direction, Layout},
|
layout::{Constraint, Direction, Layout},
|
||||||
prelude::Rect,
|
prelude::Rect,
|
||||||
};
|
};
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use tokio::sync::mpsc;
|
use tokio::sync::mpsc;
|
||||||
use tracing::{debug, info};
|
use tracing::{debug, info};
|
||||||
|
|
||||||
|
|
@ -36,11 +35,11 @@ use crate::{
|
||||||
footer::Footer, fps::FpsCounter, left::Left, popup, right::Right, title::Title, Component,
|
footer::Footer, fps::FpsCounter, left::Left, popup, right::Right, title::Title, Component,
|
||||||
},
|
},
|
||||||
config::Config,
|
config::Config,
|
||||||
|
state::{CloneSettings, Mode},
|
||||||
system::{
|
system::{
|
||||||
boot,
|
boot,
|
||||||
disk::{Disk, PartitionTableType},
|
|
||||||
diskpart::build_dest_format_script,
|
diskpart::build_dest_format_script,
|
||||||
drivers::{self, Driver},
|
drivers::{self},
|
||||||
},
|
},
|
||||||
tasks::{Task, Tasks},
|
tasks::{Task, Tasks},
|
||||||
tui::{Event, Tui},
|
tui::{Event, Tui},
|
||||||
|
|
@ -58,35 +57,13 @@ pub struct App {
|
||||||
should_suspend: bool,
|
should_suspend: bool,
|
||||||
tick_rate: f64,
|
tick_rate: f64,
|
||||||
// App
|
// App
|
||||||
|
clone: CloneSettings,
|
||||||
cur_mode: Mode,
|
cur_mode: Mode,
|
||||||
disk_index_dest: Option<usize>,
|
|
||||||
disk_index_source: Option<usize>,
|
|
||||||
disk_list: Arc<Mutex<Vec<Disk>>>,
|
|
||||||
part_index_boot: Option<usize>,
|
|
||||||
part_index_os: Option<usize>,
|
|
||||||
driver: Option<Driver>,
|
|
||||||
prev_mode: Mode,
|
prev_mode: Mode,
|
||||||
selections: Vec<Option<usize>>,
|
selections: Vec<Option<usize>>,
|
||||||
table_type: Option<PartitionTableType>,
|
|
||||||
tasks: Tasks,
|
tasks: Tasks,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Debug, Copy, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
|
||||||
pub enum Mode {
|
|
||||||
#[default]
|
|
||||||
ScanDisks,
|
|
||||||
InstallDrivers,
|
|
||||||
SelectDisks,
|
|
||||||
SelectTableType,
|
|
||||||
Confirm,
|
|
||||||
PreClone,
|
|
||||||
Clone,
|
|
||||||
SelectParts,
|
|
||||||
PostClone,
|
|
||||||
Done,
|
|
||||||
Failed,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl App {
|
impl App {
|
||||||
pub fn new(tick_rate: f64, frame_rate: f64) -> Result<Self> {
|
pub fn new(tick_rate: f64, frame_rate: f64) -> Result<Self> {
|
||||||
let (action_tx, action_rx) = mpsc::unbounded_channel();
|
let (action_tx, action_rx) = mpsc::unbounded_channel();
|
||||||
|
|
@ -112,21 +89,15 @@ impl App {
|
||||||
should_suspend: false,
|
should_suspend: false,
|
||||||
tick_rate,
|
tick_rate,
|
||||||
// App
|
// App
|
||||||
|
clone: CloneSettings::new(disk_list_arc),
|
||||||
cur_mode: Mode::ScanDisks,
|
cur_mode: Mode::ScanDisks,
|
||||||
disk_index_dest: None,
|
|
||||||
disk_index_source: None,
|
|
||||||
disk_list: disk_list_arc,
|
|
||||||
driver: None,
|
|
||||||
part_index_boot: None,
|
|
||||||
part_index_os: None,
|
|
||||||
prev_mode: Mode::ScanDisks,
|
prev_mode: Mode::ScanDisks,
|
||||||
selections: vec![None, None],
|
selections: vec![None, None],
|
||||||
table_type: None,
|
|
||||||
tasks,
|
tasks,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn next_mode(&mut self) -> Option<Mode> {
|
pub fn next_mode(&mut self) -> (Option<Mode>, Option<Mode>) {
|
||||||
let new_mode = match (self.prev_mode, self.cur_mode) {
|
let new_mode = match (self.prev_mode, self.cur_mode) {
|
||||||
(_, Mode::InstallDrivers) => Mode::ScanDisks,
|
(_, Mode::InstallDrivers) => Mode::ScanDisks,
|
||||||
(_, Mode::ScanDisks) => Mode::SelectDisks,
|
(_, Mode::ScanDisks) => Mode::SelectDisks,
|
||||||
|
|
@ -148,19 +119,22 @@ impl App {
|
||||||
(_, Mode::Confirm) => panic!("This shouldn't happen."),
|
(_, Mode::Confirm) => panic!("This shouldn't happen."),
|
||||||
};
|
};
|
||||||
|
|
||||||
if new_mode == self.cur_mode {
|
let prev_mode = {
|
||||||
None
|
|
||||||
} else {
|
|
||||||
match self.cur_mode {
|
match self.cur_mode {
|
||||||
// Update prev_mode if appropriate
|
Mode::Confirm => Some(self.prev_mode),
|
||||||
Mode::Confirm => {}
|
|
||||||
Mode::PreClone | Mode::Clone | Mode::PostClone | Mode::Done => {
|
Mode::PreClone | Mode::Clone | Mode::PostClone | Mode::Done => {
|
||||||
// Override since we're past the point of no return
|
// Override since we're past the point of no return
|
||||||
self.prev_mode = self.cur_mode;
|
Some(self.cur_mode)
|
||||||
}
|
}
|
||||||
_ => self.prev_mode = self.cur_mode,
|
_ => Some(self.cur_mode),
|
||||||
}
|
}
|
||||||
Some(new_mode)
|
};
|
||||||
|
|
||||||
|
if new_mode == self.cur_mode {
|
||||||
|
// No mode change needed
|
||||||
|
(None, None)
|
||||||
|
} else {
|
||||||
|
(prev_mode, Some(new_mode))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -181,10 +155,10 @@ impl App {
|
||||||
))?;
|
))?;
|
||||||
|
|
||||||
// Build Diskpart script to format destination disk
|
// Build Diskpart script to format destination disk
|
||||||
let disk_list = self.disk_list.lock().unwrap();
|
let disk_list = self.clone.disk_list.lock().unwrap();
|
||||||
if let Some(disk_index) = self.disk_index_dest {
|
if let Some(disk_index) = self.clone.disk_index_dest {
|
||||||
if let Some(disk) = disk_list.get(disk_index) {
|
if let Some(disk) = disk_list.get(disk_index) {
|
||||||
let table_type = self.table_type.clone().unwrap();
|
let table_type = self.clone.table_type.clone().unwrap();
|
||||||
let diskpart_script = build_dest_format_script(disk.id, &table_type);
|
let diskpart_script = build_dest_format_script(disk.id, &table_type);
|
||||||
self.tasks.add(Task::Diskpart(diskpart_script));
|
self.tasks.add(Task::Diskpart(diskpart_script));
|
||||||
}
|
}
|
||||||
|
|
@ -199,7 +173,7 @@ impl App {
|
||||||
self.config.clone_app_path.clone(),
|
self.config.clone_app_path.clone(),
|
||||||
Vec::new(),
|
Vec::new(),
|
||||||
));
|
));
|
||||||
if let Some(dest_index) = self.disk_index_dest {
|
if let Some(dest_index) = self.clone.disk_index_dest {
|
||||||
self.tasks.add(Task::UpdateDestDisk(dest_index));
|
self.tasks.add(Task::UpdateDestDisk(dest_index));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -224,12 +198,12 @@ impl App {
|
||||||
};
|
};
|
||||||
|
|
||||||
// Add actions
|
// Add actions
|
||||||
let disk_list = self.disk_list.lock().unwrap();
|
let disk_list = self.clone.disk_list.lock().unwrap();
|
||||||
if let Some(disk_index) = self.disk_index_dest {
|
if let Some(disk_index) = self.clone.disk_index_dest {
|
||||||
if let Some(disk) = disk_list.get(disk_index) {
|
if let Some(disk) = disk_list.get(disk_index) {
|
||||||
let table_type = self.table_type.clone().unwrap();
|
let table_type = self.clone.table_type.clone().unwrap();
|
||||||
let letter_boot = disk.get_part_letter(self.part_index_boot.unwrap());
|
let letter_boot = disk.get_part_letter(self.clone.part_index_boot.unwrap());
|
||||||
let letter_os = disk.get_part_letter(self.part_index_os.unwrap());
|
let letter_os = disk.get_part_letter(self.clone.part_index_os.unwrap());
|
||||||
|
|
||||||
// Safety check
|
// Safety check
|
||||||
if letter_boot.is_empty() || letter_os.is_empty() {
|
if letter_boot.is_empty() || letter_os.is_empty() {
|
||||||
|
|
@ -247,8 +221,8 @@ impl App {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Inject driver(s) (if selected)
|
// Inject driver(s) (if selected)
|
||||||
if let Some(driver) = &self.driver {
|
if let Some(driver) = &self.clone.driver {
|
||||||
if let Ok(task) = boot::inject_driver(&driver, &letter_os, &system32) {
|
if let Ok(task) = boot::inject_driver(driver, &letter_os, &system32) {
|
||||||
self.tasks.add(task);
|
self.tasks.add(task);
|
||||||
} else {
|
} else {
|
||||||
self.action_tx.send(Action::Error(format!(
|
self.action_tx.send(Action::Error(format!(
|
||||||
|
|
@ -379,12 +353,12 @@ impl App {
|
||||||
self.action_tx.send(Action::SetMode(Mode::InstallDrivers))?;
|
self.action_tx.send(Action::SetMode(Mode::InstallDrivers))?;
|
||||||
}
|
}
|
||||||
Action::SelectDriver(ref driver) => {
|
Action::SelectDriver(ref driver) => {
|
||||||
self.driver = Some(driver.clone());
|
self.clone.driver = Some(driver.clone());
|
||||||
drivers::load(&driver.inf_paths);
|
drivers::load(&driver.inf_paths);
|
||||||
self.action_tx.send(Action::NextScreen)?;
|
self.action_tx.send(Action::NextScreen)?;
|
||||||
}
|
}
|
||||||
Action::SelectTableType(ref table_type) => {
|
Action::SelectTableType(ref table_type) => {
|
||||||
self.table_type = Some(table_type.clone());
|
self.clone.table_type = Some(table_type.clone());
|
||||||
self.action_tx.send(Action::NextScreen)?;
|
self.action_tx.send(Action::NextScreen)?;
|
||||||
}
|
}
|
||||||
Action::Resize(w, h) => self.handle_resize(tui, w, h)?,
|
Action::Resize(w, h) => self.handle_resize(tui, w, h)?,
|
||||||
|
|
@ -396,22 +370,26 @@ impl App {
|
||||||
};
|
};
|
||||||
self.action_tx.send(Action::SetMode(new_mode))?;
|
self.action_tx.send(Action::SetMode(new_mode))?;
|
||||||
}
|
}
|
||||||
Action::NextScreen => {
|
Action::NextScreen => match self.next_mode() {
|
||||||
if let Some(mode) = self.next_mode() {
|
(None, None) => {}
|
||||||
|
(Some(prev), Some(next)) => {
|
||||||
|
self.prev_mode = prev;
|
||||||
|
self.cur_mode = next;
|
||||||
self.action_tx.send(Action::DismissPopup)?;
|
self.action_tx.send(Action::DismissPopup)?;
|
||||||
self.action_tx.send(Action::SetMode(mode))?;
|
self.action_tx.send(Action::SetMode(next))?;
|
||||||
}
|
}
|
||||||
}
|
_ => panic!("Failed to determine next mode"),
|
||||||
|
},
|
||||||
Action::ScanDisks => self.action_tx.send(Action::SetMode(Mode::ScanDisks))?,
|
Action::ScanDisks => self.action_tx.send(Action::SetMode(Mode::ScanDisks))?,
|
||||||
Action::Select(one, two) => {
|
Action::Select(one, two) => {
|
||||||
match self.cur_mode {
|
match self.cur_mode {
|
||||||
Mode::SelectDisks => {
|
Mode::SelectDisks => {
|
||||||
self.disk_index_source = one;
|
self.clone.disk_index_source = one;
|
||||||
self.disk_index_dest = two;
|
self.clone.disk_index_dest = two;
|
||||||
}
|
}
|
||||||
Mode::SelectParts => {
|
Mode::SelectParts => {
|
||||||
self.part_index_boot = one;
|
self.clone.part_index_boot = one;
|
||||||
self.part_index_os = two;
|
self.clone.part_index_os = two;
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ use ratatui::{
|
||||||
use tokio::sync::mpsc::UnboundedSender;
|
use tokio::sync::mpsc::UnboundedSender;
|
||||||
|
|
||||||
use super::Component;
|
use super::Component;
|
||||||
use crate::{action::Action, app::Mode, config::Config};
|
use crate::{action::Action, config::Config, state::Mode};
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct Footer {
|
pub struct Footer {
|
||||||
|
|
|
||||||
|
|
@ -25,8 +25,8 @@ use tracing::info;
|
||||||
use super::{popup, state::StatefulList, Component};
|
use super::{popup, state::StatefulList, Component};
|
||||||
use crate::{
|
use crate::{
|
||||||
action::Action,
|
action::Action,
|
||||||
app::Mode,
|
|
||||||
config::Config,
|
config::Config,
|
||||||
|
state::Mode,
|
||||||
system::{
|
system::{
|
||||||
disk::{Disk, Partition, PartitionTableType},
|
disk::{Disk, Partition, PartitionTableType},
|
||||||
drivers::{self, Driver},
|
drivers::{self, Driver},
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ use strum::Display;
|
||||||
use tokio::sync::mpsc::UnboundedSender;
|
use tokio::sync::mpsc::UnboundedSender;
|
||||||
|
|
||||||
use super::Component;
|
use super::Component;
|
||||||
use crate::{action::Action, app::Mode, config::Config};
|
use crate::{action::Action, config::Config, state::Mode};
|
||||||
|
|
||||||
#[derive(Default, Debug, Clone, PartialEq, Eq, Display, Serialize, Deserialize)]
|
#[derive(Default, Debug, Clone, PartialEq, Eq, Display, Serialize, Deserialize)]
|
||||||
pub enum Type {
|
pub enum Type {
|
||||||
|
|
|
||||||
|
|
@ -25,8 +25,8 @@ use tracing::info;
|
||||||
use super::{state::StatefulList, Component};
|
use super::{state::StatefulList, Component};
|
||||||
use crate::{
|
use crate::{
|
||||||
action::Action,
|
action::Action,
|
||||||
app::Mode,
|
|
||||||
config::Config,
|
config::Config,
|
||||||
|
state::Mode,
|
||||||
system::{
|
system::{
|
||||||
cpu::get_cpu_name,
|
cpu::get_cpu_name,
|
||||||
disk::{Disk, Partition, PartitionTableType},
|
disk::{Disk, Partition, PartitionTableType},
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ use ratatui::style::{Color, Modifier, Style};
|
||||||
use serde::{de::Deserializer, Deserialize};
|
use serde::{de::Deserializer, Deserialize};
|
||||||
use tracing::error;
|
use tracing::error;
|
||||||
|
|
||||||
use crate::{action::Action, app::Mode};
|
use crate::{action::Action, state::Mode};
|
||||||
|
|
||||||
const CONFIG: &str = include_str!("../config/config.json5");
|
const CONFIG: &str = include_str!("../config/config.json5");
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,7 @@ mod components;
|
||||||
mod config;
|
mod config;
|
||||||
mod errors;
|
mod errors;
|
||||||
mod logging;
|
mod logging;
|
||||||
|
mod state;
|
||||||
mod system;
|
mod system;
|
||||||
mod tasks;
|
mod tasks;
|
||||||
mod tests;
|
mod tests;
|
||||||
|
|
|
||||||
60
deja_vu/src/state.rs
Normal file
60
deja_vu/src/state.rs
Normal file
|
|
@ -0,0 +1,60 @@
|
||||||
|
// 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 serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use crate::system::{
|
||||||
|
disk::{Disk, PartitionTableType},
|
||||||
|
drivers::Driver,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Default, Debug, Copy, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||||
|
pub enum Mode {
|
||||||
|
#[default]
|
||||||
|
ScanDisks,
|
||||||
|
InstallDrivers,
|
||||||
|
SelectDisks,
|
||||||
|
SelectTableType,
|
||||||
|
Confirm,
|
||||||
|
PreClone,
|
||||||
|
Clone,
|
||||||
|
SelectParts,
|
||||||
|
PostClone,
|
||||||
|
Done,
|
||||||
|
Failed,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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 part_index_boot: Option<usize>,
|
||||||
|
pub part_index_os: Option<usize>,
|
||||||
|
pub driver: Option<Driver>,
|
||||||
|
pub table_type: Option<PartitionTableType>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CloneSettings {
|
||||||
|
pub fn new(disk_list: Arc<Mutex<Vec<Disk>>>) -> Self {
|
||||||
|
CloneSettings {
|
||||||
|
disk_list,
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue