Compare commits

..

No commits in common. "59b9ddbee64325c73ffccfe4dcbfc41e7eaa4af7" and "8be96d21b9509fd1e1ae338ca3d47dee1b7527f5" have entirely different histories.

50 changed files with 605 additions and 2405 deletions

593
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -1,19 +1,19 @@
# This file is part of Deja-Vu. # This file is part of Deja-vu.
# #
# Deja-Vu is free software: you can redistribute it and/or modify it # 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 # under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or # the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version. # (at your option) any later version.
# #
# Deja-Vu is distributed in the hope that it will be useful, but # Deja-vu is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of # WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the GNU General Public License for more details. # See the GNU General Public License for more details.
# #
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with Deja-Vu. If not, see <https://www.gnu.org/licenses/>. # along with Deja-vu. If not, see <https://www.gnu.org/licenses/>.
[workspace] [workspace]
members = ["core", "boot_diags", "deja_vu", "pe_menu"] members = ["core", "deja_vu", "pe_menu"]
default-members = ["boot_diags", "deja_vu", "pe_menu"] default-members = ["deja_vu", "pe_menu"]
resolver = "2" resolver = "2"

View file

@ -1,47 +0,0 @@
# 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/>.
[package]
name = "boot-diags"
authors = ["2Shirt <2xShirt@gmail.com>"]
edition = "2024"
license = "GPL"
version = "0.1.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"] }
check_elevation = "0.2.4"
[build-dependencies]
anyhow = "1.0.86"
vergen-gix = { version = "1.0.0", features = ["build", "cargo"] }

View file

@ -1,28 +0,0 @@
// 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 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()
}

File diff suppressed because it is too large Load diff

View file

@ -1,102 +0,0 @@
// 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::collections::HashMap;
pub enum Type {
Bitlocker,
BootConfigData,
FileSystem,
FilesAndFolders,
Registry,
Unknown,
}
#[derive(Debug)]
pub struct Groups {
items: HashMap<String, Line>,
order: Vec<String>,
}
impl Groups {
pub fn new() -> Self {
Groups {
items: HashMap::new(),
order: Vec::new(),
}
}
pub fn get(&self) -> Vec<&Line> {
let mut lines = Vec::new();
self.order.iter().for_each(|key| {
if let Some(line) = self.items.get(key) {
lines.push(line);
}
});
lines
}
pub fn update(&mut self, title: String, passed: bool, info: String) {
if let Some(line) = self.items.get_mut(&title) {
line.update(passed, info);
} else {
self.order.push(title.clone());
self.items.insert(
title.clone(),
Line {
title,
passed,
info: vec![info],
},
);
}
}
}
#[derive(Clone, Debug)]
pub struct Line {
pub title: String,
pub passed: bool,
pub info: Vec<String>,
}
impl Line {
pub fn update(&mut self, passed: bool, info: String) {
self.passed &= passed; // We fail if any tests in this group fail
self.info.push(info);
}
}
pub fn get_type(cmd_name: &str) -> Type {
if cmd_name == "exa" {
return Type::BootConfigData;
}
if cmd_name == "bcdedit.exe" {
return Type::BootConfigData;
}
if cmd_name == "dir" {
return Type::FilesAndFolders;
}
if cmd_name == "reg.exe" {
return Type::Registry;
}
if cmd_name == "chkdsk.exe" {
return Type::FileSystem;
}
if cmd_name == "manage-bde.exe" {
return Type::Bitlocker;
}
Type::Unknown
}

View file

@ -1,47 +0,0 @@
// 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 clap::Parser;
use color_eyre::Result;
use crate::app::App;
mod app;
mod diags;
#[tokio::main]
async fn main() -> Result<()> {
let mut msg = None;
if cfg!(windows) {
use check_elevation::is_elevated;
if !is_elevated().expect("Failed to get elevation status.") {
msg.replace("Administrator privedges required for Deja-Vu.");
}
};
match msg {
Some(text) => {
println!("{text}");
}
None => {
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(())
}

View file

@ -1,5 +1,5 @@
{ {
"app_title": "Deja-Vu", "app_title": "Deja-vu",
"clone_app_path": "C:/Program Files/Some Clone Tool/app.exe", "clone_app_path": "C:/Program Files/Some Clone Tool/app.exe",
"conemu_path": "C:/Program Files/ConEmu/ConEmu64.exe", "conemu_path": "C:/Program Files/ConEmu/ConEmu64.exe",
"keybindings": { "keybindings": {
@ -82,7 +82,7 @@
"<Ctrl-z>": "Suspend" "<Ctrl-z>": "Suspend"
}, },
"Done": { "Done": {
"<Enter>": "Process", "<Enter>": "Quit",
"<q>": "Quit", "<q>": "Quit",
"<Ctrl-d>": "Quit", "<Ctrl-d>": "Quit",
"<Ctrl-c>": "Quit", "<Ctrl-c>": "Quit",
@ -95,66 +95,11 @@
"<Ctrl-c>": "Quit", "<Ctrl-c>": "Quit",
"<Ctrl-z>": "Suspend" "<Ctrl-z>": "Suspend"
}, },
"DiagMenu": {
"<Enter>": "Process",
"<Up>": "KeyUp",
"<Down>": "KeyDown",
"<s>": "ScanDisks",
"<q>": "Quit",
"<Ctrl-d>": "Quit",
"<Ctrl-c>": "Quit",
"<Ctrl-z>": "Suspend"
},
"BootDiags": {
"<Enter>": "Process",
"<Up>": "KeyUp",
"<Down>": "KeyDown",
"<r>": "BootScan",
"<q>": "Quit",
"<Ctrl-d>": "Quit",
"<Ctrl-c>": "Quit",
"<Ctrl-z>": "Suspend"
},
"BootScan": {
"<q>": "Quit",
"<Ctrl-d>": "Quit",
"<Ctrl-c>": "Quit",
"<Ctrl-z>": "Suspend"
},
"BootSetup": {
"<Enter>": "Process",
"<Up>": "KeyUp",
"<Down>": "KeyDown",
"<q>": "Quit",
"<Ctrl-d>": "Quit",
"<Ctrl-c>": "Quit",
"<Ctrl-z>": "Suspend"
},
"InjectDrivers": {
"<Enter>": "Process",
"<Up>": "KeyUp",
"<Down>": "KeyDown",
"<q>": "Quit",
"<Ctrl-d>": "Quit",
"<Ctrl-c>": "Quit",
"<Ctrl-z>": "Suspend"
},
"SetBootMode": {
"<Enter>": "Process",
"<Up>": "KeyUp",
"<Down>": "KeyDown",
"<q>": "Quit",
"<Ctrl-d>": "Quit",
"<Ctrl-c>": "Quit",
"<Ctrl-z>": "Suspend"
},
"PEMenu": { "PEMenu": {
"<Enter>": "Process", "<Enter>": "Process",
"<Up>": "KeyUp", "<Up>": "KeyUp",
"<Down>": "KeyDown", "<Down>": "KeyDown",
"<q>": "Quit", "<q>": "Quit",
"<r>": "Restart",
"<p>": "Shutdown",
"<t>": "OpenTerminal", "<t>": "OpenTerminal",
"<Ctrl-d>": "Quit", "<Ctrl-d>": "Quit",
"<Ctrl-c>": "Quit", "<Ctrl-c>": "Quit",

View file

@ -1,22 +1,22 @@
# This file is part of Deja-Vu. # This file is part of Deja-vu.
# #
# Deja-Vu is free software: you can redistribute it and/or modify it # 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 # under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or # the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version. # (at your option) any later version.
# #
# Deja-Vu is distributed in the hope that it will be useful, but # Deja-vu is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of # WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the GNU General Public License for more details. # See the GNU General Public License for more details.
# #
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with Deja-Vu. If not, see <https://www.gnu.org/licenses/>. # along with Deja-vu. If not, see <https://www.gnu.org/licenses/>.
[package] [package]
name = "core" name = "core"
authors = ["2Shirt <2xShirt@gmail.com>"] authors = ["2Shirt <2xShirt@gmail.com>"]
edition = "2024" edition = "2021"
license = "GPL" license = "GPL"
version = "0.2.0" version = "0.2.0"
@ -42,7 +42,6 @@ lazy_static = "1.5.0"
libc = "0.2.158" libc = "0.2.158"
once_cell = "1.20.2" once_cell = "1.20.2"
pretty_assertions = "1.4.0" pretty_assertions = "1.4.0"
rand = "0.9.0"
ratatui = { version = "0.29.0", features = ["serde", "macros"] } ratatui = { version = "0.29.0", features = ["serde", "macros"] }
raw-cpuid = "11.2.0" raw-cpuid = "11.2.0"
regex = "1.11.1" regex = "1.11.1"

View file

@ -1,17 +1,17 @@
// This file is part of Deja-Vu. // This file is part of Deja-vu.
// //
// Deja-Vu is free software: you can redistribute it and/or modify it // 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 // under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or // the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version. // (at your option) any later version.
// //
// Deja-Vu is distributed in the hope that it will be useful, but // Deja-vu is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of // WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
// See the GNU General Public License for more details. // See the GNU General Public License for more details.
// //
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Deja-Vu. If not, see <https://www.gnu.org/licenses/>. // along with Deja-vu. If not, see <https://www.gnu.org/licenses/>.
// //
use anyhow::Result; use anyhow::Result;
use vergen_gix::{BuildBuilder, CargoBuilder, Emitter, GixBuilder}; use vergen_gix::{BuildBuilder, CargoBuilder, Emitter, GixBuilder};

View file

@ -1,17 +1,17 @@
// This file is part of Deja-Vu. // This file is part of Deja-vu.
// //
// Deja-Vu is free software: you can redistribute it and/or modify it // 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 // under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or // the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version. // (at your option) any later version.
// //
// Deja-Vu is distributed in the hope that it will be useful, but // Deja-vu is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of // WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
// See the GNU General Public License for more details. // See the GNU General Public License for more details.
// //
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Deja-Vu. If not, see <https://www.gnu.org/licenses/>. // along with Deja-vu. If not, see <https://www.gnu.org/licenses/>.
// //
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use strum::Display; use strum::Display;
@ -20,8 +20,6 @@ use crate::{components::popup::Type, line::DVLine, state::Mode, system::disk::Di
#[derive(Debug, Clone, PartialEq, Eq, Display, Serialize, Deserialize)] #[derive(Debug, Clone, PartialEq, Eq, Display, Serialize, Deserialize)]
pub enum Action { pub enum Action {
// App (Boot-Diags)
BootScan,
// App (Clone) // App (Clone)
Highlight(usize), Highlight(usize),
InstallDriver, InstallDriver,
@ -29,7 +27,6 @@ pub enum Action {
ScanDisks, ScanDisks,
Select(Option<usize>, Option<usize>), // indicies for (source, dest) etc Select(Option<usize>, Option<usize>), // indicies for (source, dest) etc
SelectRight(Option<usize>, Option<usize>), // indicies for right info pane SelectRight(Option<usize>, Option<usize>), // indicies for right info pane
TasksComplete,
UpdateDiskList(Vec<Disk>), UpdateDiskList(Vec<Disk>),
UpdateFooter(String), UpdateFooter(String),
UpdateLeft(String, Vec<String>, Vec<String>, usize), // (title, labels, items, select_num) UpdateLeft(String, Vec<String>, Vec<String>, usize), // (title, labels, items, select_num)
@ -38,10 +35,8 @@ pub enum Action {
// 1: For a single choice // 1: For a single choice
// 2: For two selections (obviously) // 2: For two selections (obviously)
UpdateRight(Vec<Vec<DVLine>>, usize, Vec<Vec<DVLine>>), // (labels, start_index, items) - items before start are always shown UpdateRight(Vec<Vec<DVLine>>, usize, Vec<Vec<DVLine>>), // (labels, start_index, items) - items before start are always shown
// App (PE-Menu) // App (PEMenu)
OpenTerminal, OpenTerminal,
Restart,
Shutdown,
// Screens // Screens
DismissPopup, DismissPopup,
DisplayPopup(Type, String), DisplayPopup(Type, String),

View file

@ -1,17 +1,17 @@
// This file is part of Deja-Vu. // This file is part of Deja-vu.
// //
// Deja-Vu is free software: you can redistribute it and/or modify it // 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 // under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or // the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version. // (at your option) any later version.
// //
// Deja-Vu is distributed in the hope that it will be useful, but // Deja-vu is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of // WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
// See the GNU General Public License for more details. // See the GNU General Public License for more details.
// //
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Deja-Vu. If not, see <https://www.gnu.org/licenses/>. // along with Deja-vu. If not, see <https://www.gnu.org/licenses/>.
// //
use clap::Parser; use clap::Parser;
@ -38,7 +38,6 @@ const VERSION_MESSAGE: &str = concat!(
")" ")"
); );
#[must_use]
pub fn version() -> String { pub fn version() -> String {
let author = clap::crate_authors!(); let author = clap::crate_authors!();

View file

@ -1,24 +1,23 @@
// This file is part of Deja-Vu. // This file is part of Deja-vu.
// //
// Deja-Vu is free software: you can redistribute it and/or modify it // 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 // under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or // the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version. // (at your option) any later version.
// //
// Deja-Vu is distributed in the hope that it will be useful, but // Deja-vu is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of // WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
// See the GNU General Public License for more details. // See the GNU General Public License for more details.
// //
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Deja-Vu. If not, see <https://www.gnu.org/licenses/>. // along with Deja-vu. If not, see <https://www.gnu.org/licenses/>.
#![allow(clippy::missing_errors_doc)] //
use color_eyre::Result; use color_eyre::Result;
use crossterm::event::{KeyEvent, MouseEvent}; use crossterm::event::{KeyEvent, MouseEvent};
use ratatui::{ use ratatui::{
Frame,
layout::{Rect, Size}, layout::{Rect, Size},
Frame,
}; };
use tokio::sync::mpsc::UnboundedSender; use tokio::sync::mpsc::UnboundedSender;

View file

@ -1,17 +1,17 @@
// This file is part of Deja-Vu. // This file is part of Deja-vu.
// //
// Deja-Vu is free software: you can redistribute it and/or modify it // 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 // under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or // the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version. // (at your option) any later version.
// //
// Deja-Vu is distributed in the hope that it will be useful, but // Deja-vu is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of // WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
// See the GNU General Public License for more details. // See the GNU General Public License for more details.
// //
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Deja-Vu. If not, see <https://www.gnu.org/licenses/>. // along with Deja-vu. If not, see <https://www.gnu.org/licenses/>.
// //
use color_eyre::Result; use color_eyre::Result;
use ratatui::{ use ratatui::{
@ -31,7 +31,6 @@ pub struct Footer {
} }
impl Footer { impl Footer {
#[must_use]
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
text: String::from("(q) to quit"), text: String::from("(q) to quit"),
@ -52,7 +51,6 @@ impl Component for Footer {
} }
fn update(&mut self, action: Action) -> Result<Option<Action>> { fn update(&mut self, action: Action) -> Result<Option<Action>> {
#[allow(clippy::single_match)]
match action { match action {
Action::UpdateFooter(text) => self.text = text, Action::UpdateFooter(text) => self.text = text,
_ => {} _ => {}

View file

@ -1,27 +1,27 @@
// This file is part of Deja-Vu. // This file is part of Deja-vu.
// //
// Deja-Vu is free software: you can redistribute it and/or modify it // 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 // under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or // the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version. // (at your option) any later version.
// //
// Deja-Vu is distributed in the hope that it will be useful, but // Deja-vu is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of // WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
// See the GNU General Public License for more details. // See the GNU General Public License for more details.
// //
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Deja-Vu. If not, see <https://www.gnu.org/licenses/>. // along with Deja-vu. If not, see <https://www.gnu.org/licenses/>.
// //
use std::time::Instant; use std::time::Instant;
use color_eyre::Result; use color_eyre::Result;
use ratatui::{ use ratatui::{
Frame,
layout::{Constraint, Layout, Rect}, layout::{Constraint, Layout, Rect},
style::{Style, Stylize}, style::{Style, Stylize},
text::Span, text::Span,
widgets::Paragraph, widgets::Paragraph,
Frame,
}; };
use super::Component; use super::Component;
@ -46,7 +46,6 @@ impl Default for FpsCounter {
} }
impl FpsCounter { impl FpsCounter {
#[must_use]
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
last_tick_update: Instant::now(), last_tick_update: Instant::now(),
@ -58,26 +57,24 @@ impl FpsCounter {
} }
} }
#[allow(clippy::unnecessary_wraps)]
fn app_tick(&mut self) -> Result<()> { fn app_tick(&mut self) -> Result<()> {
self.tick_count += 1; self.tick_count += 1;
let now = Instant::now(); let now = Instant::now();
let elapsed = (now - self.last_tick_update).as_secs_f64(); let elapsed = (now - self.last_tick_update).as_secs_f64();
if elapsed >= 1.0 { if elapsed >= 1.0 {
self.ticks_per_second = f64::from(self.tick_count) / elapsed; self.ticks_per_second = self.tick_count as f64 / elapsed;
self.last_tick_update = now; self.last_tick_update = now;
self.tick_count = 0; self.tick_count = 0;
} }
Ok(()) Ok(())
} }
#[allow(clippy::unnecessary_wraps)]
fn render_tick(&mut self) -> Result<()> { fn render_tick(&mut self) -> Result<()> {
self.frame_count += 1; self.frame_count += 1;
let now = Instant::now(); let now = Instant::now();
let elapsed = (now - self.last_frame_update).as_secs_f64(); let elapsed = (now - self.last_frame_update).as_secs_f64();
if elapsed >= 1.0 { if elapsed >= 1.0 {
self.frames_per_second = f64::from(self.frame_count) / elapsed; self.frames_per_second = self.frame_count as f64 / elapsed;
self.last_frame_update = now; self.last_frame_update = now;
self.frame_count = 0; self.frame_count = 0;
} }

View file

@ -1,17 +1,17 @@
// This file is part of Deja-Vu. // This file is part of Deja-vu.
// //
// Deja-Vu is free software: you can redistribute it and/or modify it // 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 // under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or // the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version. // (at your option) any later version.
// //
// Deja-Vu is distributed in the hope that it will be useful, but // Deja-vu is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of // WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
// See the GNU General Public License for more details. // See the GNU General Public License for more details.
// //
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Deja-Vu. If not, see <https://www.gnu.org/licenses/>. // along with Deja-vu. If not, see <https://www.gnu.org/licenses/>.
// //
use color_eyre::Result; use color_eyre::Result;
use crossterm::event::KeyEvent; use crossterm::event::KeyEvent;
@ -21,7 +21,7 @@ use ratatui::{
}; };
use tokio::sync::mpsc::UnboundedSender; use tokio::sync::mpsc::UnboundedSender;
use super::{Component, state::StatefulList}; use super::{state::StatefulList, Component};
use crate::{action::Action, config::Config}; use crate::{action::Action, config::Config};
#[derive(Default)] #[derive(Default)]
@ -37,7 +37,6 @@ pub struct Left {
} }
impl Left { impl Left {
#[must_use]
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
select_num: 0, select_num: 0,
@ -139,10 +138,14 @@ impl Component for Left {
.areas(area); .areas(area);
// Title // Title
let title = Paragraph::new( let title_text = if self.selections[1].is_some() || self.select_num == 1 {
Line::from(Span::styled(self.title_text.as_str(), Style::default())).centered(), "Confirm Selections"
) } else {
.block(Block::default().borders(Borders::NONE)); self.title_text.as_str()
};
let title =
Paragraph::new(Line::from(Span::styled(title_text, Style::default())).centered())
.block(Block::default().borders(Borders::NONE));
frame.render_widget(title, title_area); frame.render_widget(title, title_area);
// Body (Blank) // Body (Blank)
@ -168,7 +171,7 @@ impl Component for Left {
.map(|(index, item)| { .map(|(index, item)| {
let mut style = Style::default(); let mut style = Style::default();
let text = if self.selections[0].is_some_and(|first_index| first_index == index) { let text = if self.selections[0].is_some_and(|first_index| first_index == index) {
if let Some(label) = self.labels.first() { if let Some(label) = self.labels.get(0) {
style = style.yellow(); style = style.yellow();
label.as_str() label.as_str()
} else { } else {

View file

@ -1,20 +1,19 @@
// This file is part of Deja-Vu. // This file is part of Deja-vu.
// //
// Deja-Vu is free software: you can redistribute it and/or modify it // 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 // under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or // the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version. // (at your option) any later version.
// //
// Deja-Vu is distributed in the hope that it will be useful, but // Deja-vu is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of // WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
// See the GNU General Public License for more details. // See the GNU General Public License for more details.
// //
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Deja-Vu. If not, see <https://www.gnu.org/licenses/>. // along with Deja-vu. If not, see <https://www.gnu.org/licenses/>.
// //
use color_eyre::Result; use color_eyre::Result;
use rand::random;
use ratatui::{ use ratatui::{
prelude::*, prelude::*,
widgets::{Block, Borders, Clear, Paragraph, Wrap}, widgets::{Block, Borders, Clear, Paragraph, Wrap},
@ -43,7 +42,6 @@ pub struct Popup {
} }
impl Popup { impl Popup {
#[must_use]
pub fn new() -> Self { pub fn new() -> Self {
Self::default() Self::default()
} }
@ -97,20 +95,3 @@ impl Component for Popup {
Ok(()) Ok(())
} }
} }
#[must_use]
pub fn fortune() -> String {
String::from(match random::<u8>() / 4 {
0 => "FUN FACT\n\n\nComputers barely work.",
1 => "CRASH OVERRIDE\n\n\n\"Hack the planet!\"",
2 => "CATS\n\n\n\"All your base are belong to us!\"",
3 => "HMM\n\n\nThis has all happened before...\n\nThis will all happen again.",
4 => "CYPHER\n\n\n\"I dont even see the code. All I see is blonde, brunette, red-head.\"",
5 => "CONGRATULATIONS\n\n\nYour did it!",
6 => "DID YOU KNOW?\n\n\nmacOS includes a built-in screen reader!",
7 => "TIP OF THE DAY\n\n\nNever go full Snappy!",
8 => "WORDS OF WISDOM\n\n\n\nIts not DNS,\n\nTheres no way its DNS,\n\nIt was DNS.",
9 => "HAL 9000\n\n\n\"I'm sorry Dave, I'm afraid I can't do that.\"",
_ => "COMPLETE\n\n\nThank you for using this tool!",
})
}

View file

@ -1,17 +1,17 @@
// This file is part of Deja-Vu. // This file is part of Deja-vu.
// //
// Deja-Vu is free software: you can redistribute it and/or modify it // 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 // under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or // the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version. // (at your option) any later version.
// //
// Deja-Vu is distributed in the hope that it will be useful, but // Deja-vu is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of // WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
// See the GNU General Public License for more details. // See the GNU General Public License for more details.
// //
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Deja-Vu. If not, see <https://www.gnu.org/licenses/>. // along with Deja-vu. If not, see <https://www.gnu.org/licenses/>.
// //
use color_eyre::Result; use color_eyre::Result;
use crossterm::event::KeyEvent; use crossterm::event::KeyEvent;
@ -21,7 +21,7 @@ use ratatui::{
}; };
use tokio::sync::mpsc::UnboundedSender; use tokio::sync::mpsc::UnboundedSender;
use super::{Component, state::StatefulList}; use super::{state::StatefulList, Component};
use crate::{action::Action, config::Config, line::DVLine}; use crate::{action::Action, config::Config, line::DVLine};
#[derive(Default)] #[derive(Default)]
@ -37,7 +37,6 @@ pub struct Right {
} }
impl Right { impl Right {
#[must_use]
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
selections: vec![None, None], selections: vec![None, None],
@ -151,7 +150,7 @@ impl Component for Right {
// First selection // First selection
if let Some(first_index) = self.get_first() { if let Some(first_index) = self.get_first() {
if let Some(first_desc) = self.list.get(first_index) { if let Some(first_desc) = self.list.get(first_index) {
if let Some(label) = self.list_labels.first() { if let Some(label) = self.list_labels.get(0) {
label label
.iter() .iter()
.for_each(|dv| body_text.push(dv.as_line().bold())); .for_each(|dv| body_text.push(dv.as_line().bold()));

View file

@ -1,17 +1,17 @@
// This file is part of Deja-Vu. // This file is part of Deja-vu.
// //
// Deja-Vu is free software: you can redistribute it and/or modify it // 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 // under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or // the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version. // (at your option) any later version.
// //
// Deja-Vu is distributed in the hope that it will be useful, but // Deja-vu is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of // WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
// See the GNU General Public License for more details. // See the GNU General Public License for more details.
// //
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Deja-Vu. If not, see <https://www.gnu.org/licenses/>. // along with Deja-vu. If not, see <https://www.gnu.org/licenses/>.
// //
use std::collections::HashMap; use std::collections::HashMap;
@ -31,17 +31,14 @@ impl<T: Clone> StatefulList<T> {
self.items.clear(); self.items.clear();
} }
#[must_use]
pub fn get(&self, index: usize) -> Option<&T> { pub fn get(&self, index: usize) -> Option<&T> {
self.items.get(index) self.items.get(index)
} }
#[must_use]
pub fn get_selected(&self) -> Option<T> { pub fn get_selected(&self) -> Option<T> {
self.state.selected().map(|i| self.items[i].clone()) self.state.selected().map(|i| self.items[i].clone())
} }
#[must_use]
pub fn is_empty(&self) -> bool { pub fn is_empty(&self) -> bool {
self.items.is_empty() self.items.is_empty()
} }
@ -54,12 +51,11 @@ impl<T: Clone> StatefulList<T> {
} }
} }
#[must_use]
pub fn selected(&self) -> Option<usize> { pub fn selected(&self) -> Option<usize> {
self.state.selected() self.state.selected()
} }
pub fn select_first_item(&mut self) { fn select_first_item(&mut self) {
if self.items.is_empty() { if self.items.is_empty() {
self.state.select(None); self.state.select(None);
} else { } else {

View file

@ -1,17 +1,17 @@
// This file is part of Deja-Vu. // This file is part of Deja-vu.
// //
// Deja-Vu is free software: you can redistribute it and/or modify it // 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 // under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or // the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version. // (at your option) any later version.
// //
// Deja-Vu is distributed in the hope that it will be useful, but // Deja-vu is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of // WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
// See the GNU General Public License for more details. // See the GNU General Public License for more details.
// //
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Deja-Vu. If not, see <https://www.gnu.org/licenses/>. // along with Deja-vu. If not, see <https://www.gnu.org/licenses/>.
// //
use color_eyre::Result; use color_eyre::Result;
use ratatui::{ use ratatui::{
@ -31,7 +31,6 @@ pub struct Title {
} }
impl Title { impl Title {
#[must_use]
pub fn new(text: &str) -> Self { pub fn new(text: &str) -> Self {
Self { Self {
text: String::from(text), text: String::from(text),
@ -52,7 +51,6 @@ impl Component for Title {
} }
fn update(&mut self, action: Action) -> Result<Option<Action>> { fn update(&mut self, action: Action) -> Result<Option<Action>> {
#[allow(clippy::match_single_binding)]
match action { match action {
_ => {} _ => {}
} }

View file

@ -1,20 +1,18 @@
// This file is part of Deja-Vu. // This file is part of Deja-vu.
// //
// Deja-Vu is free software: you can redistribute it and/or modify it // 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 // under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or // the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version. // (at your option) any later version.
// //
// Deja-Vu is distributed in the hope that it will be useful, but // Deja-vu is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of // WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
// See the GNU General Public License for more details. // See the GNU General Public License for more details.
// //
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Deja-Vu. If not, see <https://www.gnu.org/licenses/>. // along with Deja-vu. If not, see <https://www.gnu.org/licenses/>.
#![allow(clippy::missing_errors_doc)] //
#![allow(clippy::missing_panics_doc)]
#![allow(clippy::ref_option)]
#![allow(dead_code)] // Remove this once you start using the code #![allow(dead_code)] // Remove this once you start using the code
use std::{collections::HashMap, env, path::PathBuf}; use std::{collections::HashMap, env, path::PathBuf};
@ -25,7 +23,7 @@ use derive_deref::{Deref, DerefMut};
use directories::ProjectDirs; use directories::ProjectDirs;
use lazy_static::lazy_static; use lazy_static::lazy_static;
use ratatui::style::{Color, Modifier, Style}; use ratatui::style::{Color, Modifier, Style};
use serde::{Deserialize, de::Deserializer}; use serde::{de::Deserializer, Deserialize};
use tracing::error; use tracing::error;
use crate::{action::Action, state::Mode}; use crate::{action::Action, state::Mode};
@ -56,15 +54,15 @@ pub struct Config {
pub styles: Styles, pub styles: Styles,
} }
pub static PROJECT_NAME: &str = "DEJA-VU"; pub static PROJECT_NAME: &'static str = "DEJA-VU";
lazy_static! { lazy_static! {
//pub static ref PROJECT_NAME: String = env!("CARGO_PKG_NAME").to_uppercase().to_string(); //pub static ref PROJECT_NAME: String = env!("CARGO_PKG_NAME").to_uppercase().to_string();
pub static ref DATA_FOLDER: Option<PathBuf> = pub static ref DATA_FOLDER: Option<PathBuf> =
env::var(format!("{PROJECT_NAME}_DATA")) env::var(format!("{}_DATA", PROJECT_NAME))
.ok() .ok()
.map(PathBuf::from); .map(PathBuf::from);
pub static ref CONFIG_FOLDER: Option<PathBuf> = pub static ref CONFIG_FOLDER: Option<PathBuf> =
env::var(format!("{PROJECT_NAME}_CONFIG")) env::var(format!("{}_CONFIG", PROJECT_NAME))
.ok() .ok()
.map(PathBuf::from); .map(PathBuf::from);
} }
@ -128,7 +126,6 @@ impl Config {
} }
} }
#[must_use]
pub fn get_data_dir() -> PathBuf { pub fn get_data_dir() -> PathBuf {
let directory = if let Some(s) = DATA_FOLDER.clone() { let directory = if let Some(s) = DATA_FOLDER.clone() {
s s
@ -140,7 +137,6 @@ pub fn get_data_dir() -> PathBuf {
directory directory
} }
#[must_use]
pub fn get_config_dir() -> PathBuf { pub fn get_config_dir() -> PathBuf {
let directory = if let Some(s) = CONFIG_FOLDER.clone() { let directory = if let Some(s) = CONFIG_FOLDER.clone() {
s s
@ -153,8 +149,8 @@ pub fn get_config_dir() -> PathBuf {
} }
fn project_directory() -> Option<ProjectDirs> { fn project_directory() -> Option<ProjectDirs> {
ProjectDirs::from("com", "Deja-Vu", "deja-vu") ProjectDirs::from("com", "Deja-vu", "deja-vu")
//ProjectDirs::from("com", "Deja-Vu", env!("CARGO_PKG_NAME")) //ProjectDirs::from("com", "Deja-vu", env!("CARGO_PKG_NAME"))
} }
#[derive(Clone, Debug, Default, Deref, DerefMut)] #[derive(Clone, Debug, Default, Deref, DerefMut)]
@ -262,7 +258,6 @@ fn parse_key_code_with_modifiers(
Ok(KeyEvent::new(c, modifiers)) Ok(KeyEvent::new(c, modifiers))
} }
#[must_use]
pub fn key_event_to_string(key_event: &KeyEvent) -> String { pub fn key_event_to_string(key_event: &KeyEvent) -> String {
let char; let char;
let key_code = match key_event.code { let key_code = match key_event.code {
@ -378,7 +373,6 @@ impl<'de> Deserialize<'de> for Styles {
} }
} }
#[must_use]
pub fn parse_style(line: &str) -> Style { pub fn parse_style(line: &str) -> Style {
let (foreground, background) = let (foreground, background) =
line.split_at(line.to_lowercase().find("on ").unwrap_or(line.len())); line.split_at(line.to_lowercase().find("on ").unwrap_or(line.len()));
@ -441,11 +435,8 @@ fn parse_color(s: &str) -> Option<Color> {
.unwrap_or_default(); .unwrap_or_default();
Some(Color::Indexed(c)) Some(Color::Indexed(c))
} else if s.contains("rgb") { } else if s.contains("rgb") {
#[allow(clippy::cast_possible_truncation)]
let red = (s.as_bytes()[3] as char).to_digit(10).unwrap_or_default() as u8; let red = (s.as_bytes()[3] as char).to_digit(10).unwrap_or_default() as u8;
#[allow(clippy::cast_possible_truncation)]
let green = (s.as_bytes()[4] as char).to_digit(10).unwrap_or_default() as u8; let green = (s.as_bytes()[4] as char).to_digit(10).unwrap_or_default() as u8;
#[allow(clippy::cast_possible_truncation)]
let blue = (s.as_bytes()[5] as char).to_digit(10).unwrap_or_default() as u8; let blue = (s.as_bytes()[5] as char).to_digit(10).unwrap_or_default() as u8;
let c = 16 + red * 36 + green * 6 + blue; let c = 16 + red * 36 + green * 6 + blue;
Some(Color::Indexed(c)) Some(Color::Indexed(c))

View file

@ -1,19 +1,18 @@
// This file is part of Deja-Vu. // This file is part of Deja-vu.
// //
// Deja-Vu is free software: you can redistribute it and/or modify it // 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 // under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or // the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version. // (at your option) any later version.
// //
// Deja-Vu is distributed in the hope that it will be useful, but // Deja-vu is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of // WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
// See the GNU General Public License for more details. // See the GNU General Public License for more details.
// //
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Deja-Vu. If not, see <https://www.gnu.org/licenses/>. // along with Deja-vu. If not, see <https://www.gnu.org/licenses/>.
#![allow(clippy::missing_errors_doc)] //
use std::env; use std::env;
use color_eyre::Result; use color_eyre::Result;

View file

@ -1,17 +1,17 @@
// This file is part of Deja-Vu. // This file is part of Deja-vu.
// //
// Deja-Vu is free software: you can redistribute it and/or modify it // 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 // under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or // the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version. // (at your option) any later version.
// //
// Deja-Vu is distributed in the hope that it will be useful, but // Deja-vu is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of // WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
// See the GNU General Public License for more details. // See the GNU General Public License for more details.
// //
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Deja-Vu. If not, see <https://www.gnu.org/licenses/>. // along with Deja-vu. If not, see <https://www.gnu.org/licenses/>.
// //
pub mod action; pub mod action;
pub mod cli; pub mod cli;

View file

@ -1,17 +1,17 @@
// This file is part of Deja-Vu. // This file is part of Deja-vu.
// //
// Deja-Vu is free software: you can redistribute it and/or modify it // 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 // under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or // the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version. // (at your option) any later version.
// //
// Deja-Vu is distributed in the hope that it will be useful, but // Deja-vu is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of // WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
// See the GNU General Public License for more details. // See the GNU General Public License for more details.
// //
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Deja-Vu. If not, see <https://www.gnu.org/licenses/>. // along with Deja-vu. If not, see <https://www.gnu.org/licenses/>.
// //
use ratatui::{ use ratatui::{
style::{Color, Style}, style::{Color, Style},
@ -30,7 +30,6 @@ pub struct DVLine {
impl DVLine { impl DVLine {
/// Convert to Line with colored span(s) /// Convert to Line with colored span(s)
#[must_use]
pub fn as_line(&self) -> Line { pub fn as_line(&self) -> Line {
let mut spans = Vec::new(); let mut spans = Vec::new();
zip(self.line_parts.clone(), self.line_colors.clone()) zip(self.line_parts.clone(), self.line_colors.clone())
@ -38,7 +37,6 @@ impl DVLine {
Line::from(spans) Line::from(spans)
} }
#[must_use]
pub fn blank() -> Self { pub fn blank() -> Self {
Self { Self {
line_parts: vec![String::new()], line_parts: vec![String::new()],
@ -47,11 +45,7 @@ impl DVLine {
} }
} }
#[must_use] pub fn get_disk_description_right(disk: &Disk) -> Vec<DVLine> {
pub fn get_disk_description_right(
disk: &Disk,
boot_os_indicies: &Option<Vec<usize>>,
) -> Vec<DVLine> {
let mut description: Vec<DVLine> = vec![ let mut description: Vec<DVLine> = vec![
DVLine { DVLine {
line_parts: vec![format!( line_parts: vec![format!(
@ -73,33 +67,15 @@ pub fn get_disk_description_right(
line_colors: vec![Color::Blue], line_colors: vec![Color::Blue],
}, },
]; ];
disk.parts_description for line in &disk.parts_description {
.iter() description.push(DVLine {
.enumerate() line_parts: vec![line.clone()],
.for_each(|(index, line)| { line_colors: vec![Color::Reset],
let mut line_parts = vec![line.clone()];
let mut line_colors = vec![Color::Reset];
if let Some(indicies) = boot_os_indicies {
let boot_index = indicies.first();
if boot_index.is_some_and(|i| i == &index) {
line_parts.push(String::from(" <-- Boot Partition"));
line_colors.push(Color::Cyan);
}
let boot_index = indicies.get(1);
if boot_index.is_some_and(|i| i == &index) {
line_parts.push(String::from(" <-- OS Partition"));
line_colors.push(Color::Cyan);
}
}
description.push(DVLine {
line_parts,
line_colors,
});
}); });
}
description description
} }
#[must_use]
pub fn get_part_description(part: &Partition) -> Vec<DVLine> { pub fn get_part_description(part: &Partition) -> Vec<DVLine> {
let description: Vec<DVLine> = vec![ let description: Vec<DVLine> = vec![
DVLine { DVLine {

View file

@ -1,22 +1,21 @@
// This file is part of Deja-Vu. // This file is part of Deja-vu.
// //
// Deja-Vu is free software: you can redistribute it and/or modify it // 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 // under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or // the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version. // (at your option) any later version.
// //
// Deja-Vu is distributed in the hope that it will be useful, but // Deja-vu is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of // WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
// See the GNU General Public License for more details. // See the GNU General Public License for more details.
// //
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Deja-Vu. If not, see <https://www.gnu.org/licenses/>. // along with Deja-vu. If not, see <https://www.gnu.org/licenses/>.
#![allow(clippy::missing_errors_doc)] //
use color_eyre::Result; use color_eyre::Result;
use tracing_error::ErrorLayer; use tracing_error::ErrorLayer;
use tracing_subscriber::{EnvFilter, fmt, prelude::*}; use tracing_subscriber::{fmt, prelude::*, EnvFilter};
use crate::config; use crate::config;

View file

@ -1,17 +1,17 @@
// This file is part of Deja-Vu. // This file is part of Deja-vu.
// //
// Deja-Vu is free software: you can redistribute it and/or modify it // 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 // under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or // the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version. // (at your option) any later version.
// //
// Deja-Vu is distributed in the hope that it will be useful, but // Deja-vu is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of // WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
// See the GNU General Public License for more details. // See the GNU General Public License for more details.
// //
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Deja-Vu. If not, see <https://www.gnu.org/licenses/>. // along with Deja-vu. If not, see <https://www.gnu.org/licenses/>.
// //
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
@ -30,13 +30,6 @@ pub enum Mode {
Home, Home,
Done, Done,
Failed, Failed,
// Boot Diags
DiagMenu,
BootDiags,
BootScan,
BootSetup,
InjectDrivers,
SetBootMode,
// Clone // Clone
ScanDisks, ScanDisks,
InstallDrivers, InstallDrivers,

View file

@ -1,17 +1,17 @@
// This file is part of Deja-Vu. // This file is part of Deja-vu.
// //
// Deja-Vu is free software: you can redistribute it and/or modify it // 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 // under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or // the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version. // (at your option) any later version.
// //
// Deja-Vu is distributed in the hope that it will be useful, but // Deja-vu is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of // WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
// See the GNU General Public License for more details. // See the GNU General Public License for more details.
// //
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Deja-Vu. If not, see <https://www.gnu.org/licenses/>. // along with Deja-vu. If not, see <https://www.gnu.org/licenses/>.
// //
pub mod boot; pub mod boot;
pub mod cpu; pub mod cpu;

View file

@ -1,43 +1,33 @@
// This file is part of Deja-Vu. // This file is part of Deja-vu.
// //
// Deja-Vu is free software: you can redistribute it and/or modify it // 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 // under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or // the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version. // (at your option) any later version.
// //
// Deja-Vu is distributed in the hope that it will be useful, but // Deja-vu is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of // WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
// See the GNU General Public License for more details. // See the GNU General Public License for more details.
// //
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Deja-Vu. If not, see <https://www.gnu.org/licenses/>. // along with Deja-vu. If not, see <https://www.gnu.org/licenses/>.
#![allow(clippy::missing_errors_doc)] //
#![allow(clippy::missing_panics_doc)]
use super::{disk::PartitionTableType, drivers::Driver}; use super::{disk::PartitionTableType, drivers::Driver};
use crate::tasks::TaskType; use crate::tasks::Task;
use color_eyre::Result; use color_eyre::Result;
use std::path::PathBuf; use std::path::PathBuf;
#[derive(Clone, Debug, Default)]
pub enum SafeMode {
#[default]
Disable,
Enable,
}
#[must_use]
pub fn configure_disk( pub fn configure_disk(
letter_boot: &str, letter_boot: &str,
letter_os: &str, letter_os: &str,
system32: &str, system32: &str,
table_type: &PartitionTableType, table_type: PartitionTableType,
) -> Vec<TaskType> { ) -> Vec<Task> {
let mut tasks = Vec::new(); let mut tasks = Vec::new();
// Create // Create
tasks.push(TaskType::CommandWait( tasks.push(Task::Command(
PathBuf::from(format!("{system32}/bcdboot.exe")), PathBuf::from(format!("{system32}/bcdboot.exe")),
vec![ vec![
format!("{letter_os}:\\Windows"), format!("{letter_os}:\\Windows"),
@ -52,8 +42,8 @@ pub fn configure_disk(
)); ));
// Update boot sector (for legacy setups) // Update boot sector (for legacy setups)
if *table_type == PartitionTableType::Legacy { if table_type == PartitionTableType::Legacy {
tasks.push(TaskType::CommandWait( tasks.push(Task::Command(
PathBuf::from(format!("{system32}/bootsect.exe")), PathBuf::from(format!("{system32}/bootsect.exe")),
vec![ vec![
String::from("/nt60"), String::from("/nt60"),
@ -65,19 +55,34 @@ pub fn configure_disk(
} }
// Lock in safe mode // Lock in safe mode
tasks.push( let bcd_path = match table_type {
set_mode(letter_boot, &SafeMode::Enable, system32, table_type) PartitionTableType::Guid => {
.expect("Failed to create set_mode task."), format!("{letter_boot}:\\EFI\\Microsoft\\Boot\\BCD")
); }
PartitionTableType::Legacy => {
format!("{letter_boot}:\\Boot\\BCD")
}
};
tasks.push(Task::Command(
PathBuf::from(format!("{system32}/bcdedit.exe")),
vec![
String::from("/store"),
bcd_path,
String::from("/set"),
String::from("{default}"),
String::from("safeboot"),
String::from("minimal"),
],
));
// Done // Done
tasks tasks
} }
pub fn inject_driver(driver: &Driver, letter_os: &str, system32: &str) -> Result<TaskType> { pub fn inject_driver(driver: &Driver, letter_os: &str, system32: &str) -> Result<Task> {
//if let Some(driver_path_str) = driver.path.to_str() { //if let Some(driver_path_str) = driver.path.to_str() {
let driver_path = driver.path.to_str().unwrap(); let driver_path = driver.path.to_str().unwrap();
Ok(TaskType::CommandWait( Ok(Task::Command(
PathBuf::from(format!("{system32}/dism.exe")), PathBuf::from(format!("{system32}/dism.exe")),
vec![ vec![
format!("/image:{letter_os}:\\"), format!("/image:{letter_os}:\\"),
@ -87,39 +92,3 @@ pub fn inject_driver(driver: &Driver, letter_os: &str, system32: &str) -> Result
], ],
)) ))
} }
pub fn set_mode(
letter_boot: &str,
mode: &SafeMode,
system32: &str,
table_type: &PartitionTableType,
) -> Result<TaskType> {
let bcd_path = match table_type {
PartitionTableType::Guid => {
format!("{letter_boot}:\\EFI\\Microsoft\\Boot\\BCD")
}
PartitionTableType::Legacy => {
format!("{letter_boot}:\\Boot\\BCD")
}
};
// Build CommandWait
let mut cmd_args = vec![String::from("/store"), bcd_path];
match mode {
SafeMode::Disable => {
cmd_args.push(String::from("/deletevalue"));
cmd_args.push(String::from("{default}"));
cmd_args.push(String::from("safeboot"));
}
SafeMode::Enable => {
cmd_args.push(String::from("/set"));
cmd_args.push(String::from("{default}"));
cmd_args.push(String::from("safeboot"));
cmd_args.push(String::from("minimal"));
}
}
Ok(TaskType::CommandWait(
PathBuf::from(format!("{system32}/bcdedit.exe")),
cmd_args,
))
}

View file

@ -1,17 +1,17 @@
// This file is part of Deja-Vu. // This file is part of Deja-vu.
// //
// Deja-Vu is free software: you can redistribute it and/or modify it // 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 // under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or // the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version. // (at your option) any later version.
// //
// Deja-Vu is distributed in the hope that it will be useful, but // Deja-vu is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of // WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
// See the GNU General Public License for more details. // See the GNU General Public License for more details.
// //
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Deja-Vu. If not, see <https://www.gnu.org/licenses/>. // along with Deja-vu. If not, see <https://www.gnu.org/licenses/>.
// //
#[must_use] #[must_use]
pub fn get_cpu_name() -> String { pub fn get_cpu_name() -> String {

View file

@ -1,17 +1,17 @@
// This file is part of Deja-Vu. // This file is part of Deja-vu.
// //
// Deja-Vu is free software: you can redistribute it and/or modify it // 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 // under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or // the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version. // (at your option) any later version.
// //
// Deja-Vu is distributed in the hope that it will be useful, but // Deja-vu is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of // WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
// See the GNU General Public License for more details. // See the GNU General Public License for more details.
// //
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Deja-Vu. If not, see <https://www.gnu.org/licenses/>. // along with Deja-vu. If not, see <https://www.gnu.org/licenses/>.
// //
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::{ use std::{
@ -67,7 +67,6 @@ impl Disk {
} }
} }
#[must_use]
pub fn get_part_letter(&self, part_index: usize) -> String { pub fn get_part_letter(&self, part_index: usize) -> String {
// Used to get Boot and OS letters // Used to get Boot and OS letters
if let Some(part) = self.parts.get(part_index) { if let Some(part) = self.parts.get(part_index) {
@ -77,12 +76,10 @@ impl Disk {
} }
} }
#[must_use]
pub fn get_parts(&self) -> Vec<Partition> { pub fn get_parts(&self) -> Vec<Partition> {
self.parts.clone() self.parts.clone()
} }
#[must_use]
pub fn num_parts(&self) -> usize { pub fn num_parts(&self) -> usize {
self.parts.len() self.parts.len()
} }

View file

@ -1,19 +1,18 @@
// This file is part of Deja-Vu. // This file is part of Deja-vu.
// //
// Deja-Vu is free software: you can redistribute it and/or modify it // 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 // under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or // the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version. // (at your option) any later version.
// //
// Deja-Vu is distributed in the hope that it will be useful, but // Deja-vu is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of // WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
// See the GNU General Public License for more details. // See the GNU General Public License for more details.
// //
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Deja-Vu. If not, see <https://www.gnu.org/licenses/>. // along with Deja-vu. If not, see <https://www.gnu.org/licenses/>.
#![allow(clippy::missing_panics_doc)] //
use std::{ use std::{
collections::HashMap, collections::HashMap,
fs::File, fs::File,
@ -27,7 +26,7 @@ use tempfile::tempdir;
use tracing::{info, warn}; use tracing::{info, warn};
use crate::system::disk::{ use crate::system::disk::{
Disk, Partition, PartitionTableType, get_disk_serial_number, string_to_bytes, get_disk_serial_number, string_to_bytes, Disk, Partition, PartitionTableType,
}; };
static DEFAULT_MAX_DISKS: usize = 8; static DEFAULT_MAX_DISKS: usize = 8;
@ -130,7 +129,7 @@ pub fn get_partition_details(
#[must_use] #[must_use]
pub fn build_dest_format_script(disk_id: usize, part_type: &PartitionTableType) -> String { pub fn build_dest_format_script(disk_id: usize, part_type: &PartitionTableType) -> String {
let disk_id = format!("{disk_id}"); let disk_id = format!("{disk_id}");
let mut script = vec!["automount enable noerr", "select disk {disk_id}", "clean"]; let mut script = vec!["select disk {disk_id}", "clean"];
match part_type { match part_type {
PartitionTableType::Guid => { PartitionTableType::Guid => {
script.push("convert gpt"); script.push("convert gpt");

View file

@ -1,17 +1,17 @@
// This file is part of Deja-Vu. // This file is part of Deja-vu.
// //
// Deja-Vu is free software: you can redistribute it and/or modify it // 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 // under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or // the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version. // (at your option) any later version.
// //
// Deja-Vu is distributed in the hope that it will be useful, but // Deja-vu is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of // WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
// See the GNU General Public License for more details. // See the GNU General Public License for more details.
// //
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Deja-Vu. If not, see <https://www.gnu.org/licenses/>. // along with Deja-vu. If not, see <https://www.gnu.org/licenses/>.
// //
use std::{env, fmt, fs::read_dir, path::PathBuf, process::Command}; use std::{env, fmt, fs::read_dir, path::PathBuf, process::Command};

View file

@ -1,27 +1,25 @@
// This file is part of Deja-Vu. // This file is part of Deja-vu.
// //
// Deja-Vu is free software: you can redistribute it and/or modify it // 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 // under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or // the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version. // (at your option) any later version.
// //
// Deja-Vu is distributed in the hope that it will be useful, but // Deja-vu is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of // WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
// See the GNU General Public License for more details. // See the GNU General Public License for more details.
// //
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Deja-Vu. If not, see <https://www.gnu.org/licenses/>. // along with Deja-vu. If not, see <https://www.gnu.org/licenses/>.
#![allow(clippy::missing_errors_doc)] //
#![allow(clippy::missing_panics_doc)]
use std::{ use std::{
collections::VecDeque, collections::VecDeque,
fmt,
path::PathBuf, path::PathBuf,
process::{Command, Stdio}, process::{Command, Stdio},
sync::{Arc, Mutex}, sync::{Arc, Mutex},
thread::{self, JoinHandle, sleep}, thread::{self, sleep, JoinHandle},
time::Duration, time::Duration,
}; };
@ -36,70 +34,23 @@ use crate::{
}; };
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub enum TaskResult { pub enum Task {
Error(String), Command(PathBuf, Vec<String>), // (command, args)
Output(String, String, bool), // stdout, stderr, success Diskpart(String), // (script_as_string)
}
#[derive(Clone, Debug)]
pub enum TaskType {
CommandNoWait(PathBuf, Vec<String>), // (command, args)
CommandWait(PathBuf, Vec<String>), // (command, args)
Diskpart(String), // (script_as_string)
ScanDisks, ScanDisks,
Sleep, Sleep,
UpdateDestDisk(usize), // (disk_index) UpdateDestDisk(usize), // (disk_index)
UpdateDiskList, UpdateDiskList,
} }
impl fmt::Display for TaskType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
TaskType::CommandNoWait(cmd_path, _) | TaskType::CommandWait(cmd_path, _) => {
write!(
f,
"Command(\"{}\")",
cmd_path.file_name().unwrap().to_string_lossy()
)
}
TaskType::Diskpart(_) => {
write!(f, "Diskpart")
}
TaskType::ScanDisks => write!(f, "ScanDisks"),
TaskType::Sleep => write!(f, "Sleep"),
TaskType::UpdateDestDisk(_) => write!(f, "UpdateDestDisk"),
TaskType::UpdateDiskList => write!(f, "UpdateDiskList"),
}
}
}
#[derive(Debug)]
pub struct Task {
pub handle: Option<JoinHandle<()>>,
pub result: Option<TaskResult>,
pub task_type: TaskType,
}
impl Task {
#[must_use]
pub fn new(task_type: TaskType) -> Task {
Task {
handle: None,
result: None,
task_type,
}
}
}
#[derive(Debug)] #[derive(Debug)]
pub struct Tasks { pub struct Tasks {
action_tx: mpsc::UnboundedSender<Action>, action_tx: mpsc::UnboundedSender<Action>,
disk_list: Arc<Mutex<Vec<disk::Disk>>>, disk_list: Arc<Mutex<Vec<disk::Disk>>>,
cur_handle: Option<JoinHandle<()>>, handle: Option<JoinHandle<()>>,
cur_task: Option<Task>,
task_list: VecDeque<Task>, task_list: VecDeque<Task>,
task_rx: mpsc::UnboundedReceiver<TaskResult>, task_rx: mpsc::UnboundedReceiver<Action>, // Used to forward Actions from Tasks to App
task_tx: mpsc::UnboundedSender<TaskResult>, task_tx: mpsc::UnboundedSender<Action>, // Used to forward Actions from Tasks to App
} }
impl Tasks { impl Tasks {
@ -111,186 +62,159 @@ impl Tasks {
Tasks { Tasks {
action_tx, action_tx,
disk_list: disk_list_arc, disk_list: disk_list_arc,
cur_handle: None, handle: None,
cur_task: None,
task_list: VecDeque::new(), task_list: VecDeque::new(),
task_rx, task_rx,
task_tx, task_tx,
} }
} }
pub fn add(&mut self, task_type: TaskType) { pub fn add(&mut self, task: Task) {
info!("Adding task: {:?}", &task_type); info!("Adding task: {:?}", &task);
self.task_list.push_back(Task::new(task_type)); self.task_list.push_back(task);
} }
#[must_use]
pub fn idle(&self) -> bool { pub fn idle(&self) -> bool {
self.cur_handle.is_none() self.handle.is_none()
} }
pub fn poll(&mut self) -> Result<Option<Task>> { pub fn poll(&mut self) -> Result<()> {
let mut return_task: Option<Task> = None; // Forward any actions to main app
// Handle task channel item(s) if let Ok(action) = self.task_rx.try_recv() {
if let Ok(result) = self.task_rx.try_recv() { let result = self.action_tx.send(action.clone());
if let Some(mut task) = self.cur_task.take() { assert!(result.is_ok(), "Failed to send Action: {action:?}");
task.result.replace(result);
self.cur_task.replace(task);
}
} }
// Check status of current task (if one is running). // Check status of current task (if one is running).
// NOTE: Action::TasksComplete is sent once all tasks are complete // NOTE: Action::NextScreen is sent once all tasks are complete
if let Some(task_handle) = self.cur_handle.take() { if let Some(handle) = self.handle.take() {
if task_handle.is_finished() { if handle.is_finished() {
// Need to return task with handle
if let Some(mut cur_task) = self.cur_task.take() {
cur_task.handle = Some(task_handle);
return_task = Some(cur_task);
}
if self.task_list.is_empty() { if self.task_list.is_empty() {
// No tasks remain // No tasks remain
self.action_tx.send(Action::TasksComplete)?; self.task_tx.send(Action::NextScreen)?;
} else { } else {
// Start next task // Start next task
self.start()?; self.start()?;
} }
} else { } else {
// TaskType not complete, return handle // Task not complete, return handle
self.cur_handle.replace(task_handle); self.handle = Some(handle);
} }
} else if !self.task_list.is_empty() { } else if !self.task_list.is_empty() {
// No current task but one is available // No current task but one is available
self.start()?; self.start()?;
} }
Ok(return_task) Ok(())
} }
pub fn start(&mut self) -> Result<()> { pub fn start(&mut self) -> Result<()> {
self.cur_task = self.task_list.pop_front(); if let Some(task) = self.task_list.pop_front() {
if let Some(task) = self.cur_task.take() { let task_str = format!("{task:?}");
let task_tx = self.task_tx.clone(); let task_tx = self.task_tx.clone();
match task.task_type { match task {
TaskType::CommandNoWait(ref cmd_path, ref cmd_args) => { Task::Command(ref cmd_path, ref cmd_args) => {
self.cur_handle = None; let cmd_path = cmd_path.clone();
run_task_command(cmd_path.clone(), cmd_args.clone(), task_tx); let cmd_args = cmd_args.clone();
if cfg!(windows) {
self.handle = Some(thread::spawn(move || {
let result = Command::new(cmd_path)
.args(cmd_args)
.stdout(Stdio::piped())
.output();
if let Some(action) = match result {
Ok(output) => {
if output.status.success() {
None
} else {
// Command returned an error status
let mut msg = String::new();
if let Ok(stdout) = String::from_utf8(output.stdout) {
msg = String::from(stdout.trim());
}
if msg.is_empty() {
msg = String::from("Generic error");
}
Some(Action::Error(format!("Command failed: {msg}",)))
}
}
Err(err) => {
Some(Action::Error(format!("Failed to run command: {err:?}")))
}
} {
let msg = format!("{:?}", &action);
let result = task_tx.send(action);
assert!(result.is_ok(), "Failed to send Action: {msg}");
}
}));
} else {
// Simulate task if not running under Windows
self.handle = Some(thread::spawn(|| sleep(Duration::from_millis(250))));
}
} }
TaskType::CommandWait(ref cmd_path, ref cmd_args) => { Task::Diskpart(ref script) => {
self.cur_handle = Some(run_task_command( if cfg!(windows) {
cmd_path.clone(), let script = String::from(script);
cmd_args.clone(), self.handle = Some(thread::spawn(move || {
task_tx, let output = diskpart::run_script_raw(script.as_str());
)); if !output.status.success()
&& task_tx
.send(Action::Error(String::from(
"Diskpart script returned an error",
)))
.is_err()
{
panic!("Failed to send Action: {task_str:?}");
}
}));
} else {
// Simulate task if not running under Windows
self.handle = Some(thread::spawn(|| sleep(Duration::from_millis(250))));
}
} }
TaskType::Diskpart(ref script) => { Task::ScanDisks => {
self.cur_handle = Some(run_task_diskpart(script, task_tx));
}
TaskType::ScanDisks => {
let disk_list_arc = self.disk_list.clone(); let disk_list_arc = self.disk_list.clone();
// Queue UpdateDiskList for various components // Queue UpdateDiskList for various components
self.add(TaskType::UpdateDiskList); self.add(Task::UpdateDiskList);
self.cur_handle = Some(thread::spawn(move || { self.handle = Some(thread::spawn(move || {
let mut disks = disk_list_arc.lock().unwrap(); let mut disks = disk_list_arc.lock().unwrap();
*disks = disk::get_disks(); *disks = disk::get_disks();
})); }));
} }
TaskType::Sleep => { Task::Sleep => {
self.cur_handle = Some(thread::spawn(|| sleep(Duration::from_millis(250)))); self.handle = Some(thread::spawn(|| sleep(Duration::from_millis(250))));
} }
TaskType::UpdateDestDisk(index) => { Task::UpdateDestDisk(index) => {
self.action_tx.send(Action::DisplayPopup( self.action_tx.send(Action::DisplayPopup(
popup::Type::Info, popup::Type::Info,
String::from("Refreshing disk info"), String::from("Refreshing disk info"),
))?; ))?;
// Queue UpdateDiskList for various components // Queue UpdateDiskList for various components
self.add(TaskType::Sleep); self.add(Task::Sleep);
self.add(TaskType::UpdateDiskList); self.add(Task::UpdateDiskList);
// Update destination disk ~in-place // Update destination disk ~in-place
let disk_list_arc = self.disk_list.clone(); let disk_list_arc = self.disk_list.clone();
self.cur_handle = Some(thread::spawn(move || { self.handle = Some(thread::spawn(move || {
let mut disks = disk_list_arc.lock().unwrap(); let mut disks = disk_list_arc.lock().unwrap();
let old_disk = &mut disks[index]; let old_disk = &mut disks[index];
disks[index] = disk::refresh_disk_info(old_disk); disks[index] = disk::refresh_disk_info(old_disk);
})); }));
} }
TaskType::UpdateDiskList => { Task::UpdateDiskList => {
let disks = self.disk_list.lock().unwrap(); let disks = self.disk_list.lock().unwrap();
let disks_copy = disks.clone(); let disks_copy = disks.clone();
let action_tx = self.action_tx.clone(); let action_tx = self.action_tx.clone();
self.cur_handle = Some(thread::spawn(move || { self.handle = Some(thread::spawn(move || {
if let Err(err) = action_tx.send(Action::UpdateDiskList(disks_copy)) { if let Err(err) = action_tx.send(Action::UpdateDiskList(disks_copy)) {
panic!("Failed to send Action: {err:?}"); panic!("Failed to send Action: {err:?}");
} }
})); }));
} }
} }
// Done
self.cur_task.replace(task);
} }
Ok(()) Ok(())
} }
} }
fn parse_bytes_as_str(bytes: Vec<u8>) -> String {
match String::from_utf8(bytes) {
Ok(s) => s.trim().to_string(),
Err(_) => String::from("Failed to parse bytes as UTF-8 text"),
}
}
fn run_task_command(
cmd_path: PathBuf,
cmd_args: Vec<String>,
task_tx: mpsc::UnboundedSender<TaskResult>,
) -> JoinHandle<()> {
if cfg!(windows) {
thread::spawn(move || {
let result = Command::new(cmd_path)
.args(cmd_args)
.stdout(Stdio::piped())
.output();
match result {
Err(e) => {
task_tx
.send(TaskResult::Error(format!("{:?}", &e)))
.expect("Failed to propegate error?");
}
Ok(output) => {
let stderr = parse_bytes_as_str(output.stderr.clone());
let stdout = parse_bytes_as_str(output.stdout.clone());
let task_result = TaskResult::Output(stdout, stderr, output.status.success());
let err_str = format!("Failed to send TaskResult: {:?}", &task_result);
task_tx
.send(task_result)
.unwrap_or_else(|_| panic!("{}", err_str));
}
}
})
} else {
// Simulate task if not running under Windows
thread::spawn(|| sleep(Duration::from_millis(250)))
}
}
fn run_task_diskpart(script: &str, task_tx: mpsc::UnboundedSender<TaskResult>) -> JoinHandle<()> {
if cfg!(windows) {
let script = script.to_owned();
thread::spawn(move || {
let output = diskpart::run_script_raw(&script);
let stderr = parse_bytes_as_str(output.stderr.clone());
let stdout = parse_bytes_as_str(output.stdout.clone());
let task_result = TaskResult::Output(stdout, stderr, output.status.success());
let err_str = format!("Failed to send TaskResult: {:?}", &task_result);
task_tx
.send(task_result)
.unwrap_or_else(|_| panic!("{}", err_str));
})
} else {
// Simulate task if not running under Windows
thread::spawn(|| sleep(Duration::from_millis(250)))
}
}

View file

@ -1,17 +1,17 @@
// This file is part of Deja-Vu. // This file is part of Deja-vu.
// //
// Deja-Vu is free software: you can redistribute it and/or modify it // 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 // under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or // the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version. // (at your option) any later version.
// //
// Deja-Vu is distributed in the hope that it will be useful, but // Deja-vu is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of // WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
// See the GNU General Public License for more details. // See the GNU General Public License for more details.
// //
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Deja-Vu. If not, see <https://www.gnu.org/licenses/>. // along with Deja-vu. If not, see <https://www.gnu.org/licenses/>.
// //
pub mod sample_output; pub mod sample_output;

View file

@ -1,17 +1,17 @@
// This file is part of Deja-Vu. // This file is part of Deja-vu.
// //
// Deja-Vu is free software: you can redistribute it and/or modify it // 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 // under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or // the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version. // (at your option) any later version.
// //
// Deja-Vu is distributed in the hope that it will be useful, but // Deja-vu is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of // WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
// See the GNU General Public License for more details. // See the GNU General Public License for more details.
// //
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Deja-Vu. If not, see <https://www.gnu.org/licenses/>. // along with Deja-vu. If not, see <https://www.gnu.org/licenses/>.
// //
#[allow(dead_code)] #[allow(dead_code)]
pub static DETAIL_DISK_GPT: &str = "Disk 2 is now the selected disk. pub static DETAIL_DISK_GPT: &str = "Disk 2 is now the selected disk.

View file

@ -1,22 +1,22 @@
// This file is part of Deja-Vu. // This file is part of Deja-vu.
// //
// Deja-Vu is free software: you can redistribute it and/or modify it // 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 // under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or // the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version. // (at your option) any later version.
// //
// Deja-Vu is distributed in the hope that it will be useful, but // Deja-vu is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of // WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
// See the GNU General Public License for more details. // See the GNU General Public License for more details.
// //
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Deja-Vu. If not, see <https://www.gnu.org/licenses/>. // along with Deja-vu. If not, see <https://www.gnu.org/licenses/>.
#![allow(clippy::missing_errors_doc)] //
#![allow(dead_code)] // Remove this once you start using the code #![allow(dead_code)] // Remove this once you start using the code
use std::{ use std::{
io::{Stdout, stdout}, io::{stdout, Stdout},
ops::{Deref, DerefMut}, ops::{Deref, DerefMut},
time::Duration, time::Duration,
}; };
@ -85,25 +85,21 @@ impl Tui {
}) })
} }
#[must_use]
pub fn tick_rate(mut self, tick_rate: f64) -> Self { pub fn tick_rate(mut self, tick_rate: f64) -> Self {
self.tick_rate = tick_rate; self.tick_rate = tick_rate;
self self
} }
#[must_use]
pub fn frame_rate(mut self, frame_rate: f64) -> Self { pub fn frame_rate(mut self, frame_rate: f64) -> Self {
self.frame_rate = frame_rate; self.frame_rate = frame_rate;
self self
} }
#[must_use]
pub fn mouse(mut self, mouse: bool) -> Self { pub fn mouse(mut self, mouse: bool) -> Self {
self.mouse = mouse; self.mouse = mouse;
self self
} }
#[must_use]
pub fn paste(mut self, paste: bool) -> Self { pub fn paste(mut self, paste: bool) -> Self {
self.paste = paste; self.paste = paste;
self self
@ -139,7 +135,7 @@ impl Tui {
.expect("failed to send init event"); .expect("failed to send init event");
loop { loop {
let event = tokio::select! { let event = tokio::select! {
() = cancellation_token.cancelled() => { _ = cancellation_token.cancelled() => {
break; break;
} }
_ = tick_interval.tick() => Event::Tick, _ = tick_interval.tick() => Event::Tick,
@ -152,7 +148,7 @@ impl Tui {
CrosstermEvent::FocusLost => Event::FocusLost, CrosstermEvent::FocusLost => Event::FocusLost,
CrosstermEvent::FocusGained => Event::FocusGained, CrosstermEvent::FocusGained => Event::FocusGained,
CrosstermEvent::Paste(s) => Event::Paste(s), CrosstermEvent::Paste(s) => Event::Paste(s),
CrosstermEvent::Key(_) => continue, // ignore other events _ => continue, // ignore other events
} }
Some(Err(_)) => Event::Error, Some(Err(_)) => Event::Error,
None => break, // the event stream has stopped and will not produce any more events None => break, // the event stream has stopped and will not produce any more events

View file

@ -1,22 +1,22 @@
# This file is part of Deja-Vu. # This file is part of Deja-vu.
# #
# Deja-Vu is free software: you can redistribute it and/or modify it # 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 # under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or # the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version. # (at your option) any later version.
# #
# Deja-Vu is distributed in the hope that it will be useful, but # Deja-vu is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of # WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the GNU General Public License for more details. # See the GNU General Public License for more details.
# #
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with Deja-Vu. If not, see <https://www.gnu.org/licenses/>. # along with Deja-vu. If not, see <https://www.gnu.org/licenses/>.
[package] [package]
name = "deja-vu" name = "deja-vu"
authors = ["2Shirt <2xShirt@gmail.com>"] authors = ["2Shirt <2xShirt@gmail.com>"]
edition = "2024" edition = "2021"
license = "GPL" license = "GPL"
version = "0.2.0" version = "0.2.0"
@ -39,7 +39,7 @@ tokio-util = "0.7.11"
tracing = "0.1.41" tracing = "0.1.41"
tracing-error = "0.2.0" tracing-error = "0.2.0"
tracing-subscriber = { version = "0.3.18", features = ["env-filter", "serde"] } tracing-subscriber = { version = "0.3.18", features = ["env-filter", "serde"] }
check_elevation = "0.2.4" rand = "0.8.5"
[build-dependencies] [build-dependencies]
anyhow = "1.0.86" anyhow = "1.0.86"

View file

@ -1,17 +1,17 @@
// This file is part of Deja-Vu. // This file is part of Deja-vu.
// //
// Deja-Vu is free software: you can redistribute it and/or modify it // 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 // under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or // the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version. // (at your option) any later version.
// //
// Deja-Vu is distributed in the hope that it will be useful, but // Deja-vu is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of // WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
// See the GNU General Public License for more details. // See the GNU General Public License for more details.
// //
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Deja-Vu. If not, see <https://www.gnu.org/licenses/>. // along with Deja-vu. If not, see <https://www.gnu.org/licenses/>.
// //
use anyhow::Result; use anyhow::Result;
use vergen_gix::{BuildBuilder, CargoBuilder, Emitter, GixBuilder}; use vergen_gix::{BuildBuilder, CargoBuilder, Emitter, GixBuilder};

View file

@ -1,42 +1,42 @@
// This file is part of Deja-Vu. // This file is part of Deja-vu.
// //
// Deja-Vu is free software: you can redistribute it and/or modify it // 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 // under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or // the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version. // (at your option) any later version.
// //
// Deja-Vu is distributed in the hope that it will be useful, but // Deja-vu is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of // WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
// See the GNU General Public License for more details. // See the GNU General Public License for more details.
// //
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Deja-Vu. If not, see <https://www.gnu.org/licenses/>. // along with Deja-vu. If not, see <https://www.gnu.org/licenses/>.
use core::{ use core::{
action::Action, action::Action,
components::{ components::{
Component, footer::Footer, fps::FpsCounter, left::Left, popup, right::Right, footer::Footer, fps::FpsCounter, left::Left, popup, right::Right, state::StatefulList,
state::StatefulList, title::Title, title::Title, Component,
}, },
config::Config, config::Config,
line::{DVLine, get_disk_description_right, get_part_description}, line::{get_disk_description_right, get_part_description, DVLine},
state::{CloneSettings, Mode}, state::{CloneSettings, Mode},
system::{ system::{
boot, cpu::get_cpu_name, disk::PartitionTableType, diskpart::build_dest_format_script, boot, cpu::get_cpu_name, disk::PartitionTableType, diskpart::build_dest_format_script,
drivers, drivers,
}, },
tasks::{Task, TaskResult, TaskType, Tasks}, tasks::{Task, Tasks},
tui::{Event, Tui}, tui::{Event, Tui},
}; };
use std::{ use std::{
env, env,
iter::zip, iter::zip,
path::PathBuf,
sync::{Arc, Mutex}, sync::{Arc, Mutex},
}; };
use color_eyre::Result; use color_eyre::Result;
use rand::random;
use ratatui::{ use ratatui::{
crossterm::event::KeyEvent, crossterm::event::KeyEvent,
layout::{Constraint, Direction, Layout}, layout::{Constraint, Direction, Layout},
@ -100,7 +100,7 @@ impl App {
} }
pub fn prev_mode(&mut self) -> Option<Mode> { pub fn prev_mode(&mut self) -> Option<Mode> {
match self.cur_mode { let new_mode = match self.cur_mode {
Mode::Home => Some(Mode::Home), Mode::Home => Some(Mode::Home),
Mode::Failed => Some(Mode::Failed), Mode::Failed => Some(Mode::Failed),
Mode::Done => Some(Mode::Done), Mode::Done => Some(Mode::Done),
@ -115,19 +115,15 @@ impl App {
| Mode::Clone | Mode::Clone
| Mode::PostClone => None, | Mode::PostClone => None,
// Invalid states // Invalid states
Mode::BootDiags Mode::PEMenu => panic!("This shouldn't happen?"),
| Mode::BootScan };
| Mode::BootSetup new_mode
| Mode::DiagMenu
| Mode::InjectDrivers
| Mode::PEMenu
| Mode::SetBootMode => panic!("This shouldn't happen?"),
}
} }
pub fn next_mode(&mut self) -> Option<Mode> { pub fn next_mode(&mut self) -> Option<Mode> {
let new_mode = match self.cur_mode { let new_mode = match self.cur_mode {
Mode::Home | Mode::InstallDrivers => Mode::ScanDisks, Mode::Home => Mode::ScanDisks,
Mode::InstallDrivers => Mode::ScanDisks,
Mode::ScanDisks => Mode::SelectDisks, Mode::ScanDisks => Mode::SelectDisks,
Mode::SelectDisks => Mode::SelectTableType, Mode::SelectDisks => Mode::SelectTableType,
Mode::SelectTableType => Mode::Confirm, Mode::SelectTableType => Mode::Confirm,
@ -138,13 +134,7 @@ impl App {
Mode::PostClone | Mode::Done => Mode::Done, Mode::PostClone | Mode::Done => Mode::Done,
Mode::Failed => Mode::Failed, Mode::Failed => Mode::Failed,
// Invalid states // Invalid states
Mode::BootDiags Mode::PEMenu => panic!("This shouldn't happen?"),
| Mode::BootScan
| Mode::BootSetup
| Mode::DiagMenu
| Mode::InjectDrivers
| Mode::PEMenu
| Mode::SetBootMode => panic!("This shouldn't happen?"),
}; };
if new_mode == self.cur_mode { if new_mode == self.cur_mode {
@ -163,7 +153,7 @@ impl App {
Mode::ScanDisks => { Mode::ScanDisks => {
self.prev_mode = self.cur_mode; self.prev_mode = self.cur_mode;
if self.tasks.idle() { if self.tasks.idle() {
self.tasks.add(TaskType::ScanDisks); self.tasks.add(Task::ScanDisks);
} }
self.action_tx.send(Action::DisplayPopup( self.action_tx.send(Action::DisplayPopup(
popup::Type::Info, popup::Type::Info,
@ -176,22 +166,13 @@ impl App {
String::from("Formatting destination disk"), String::from("Formatting destination disk"),
))?; ))?;
// Get System32 path
let system32 = get_system32_path(&self.action_tx);
// (Re)Enable volume mounting
self.tasks.add(TaskType::CommandWait(
PathBuf::from(format!("{system32}/mountvol.exe")),
vec![String::from("/e")],
));
// Build Diskpart script to format destination disk // Build Diskpart script to format destination disk
let disk_list = self.clone.disk_list.lock().unwrap(); let disk_list = self.clone.disk_list.lock().unwrap();
if let Some(disk_index) = self.clone.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.clone.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(TaskType::Diskpart(diskpart_script)); self.tasks.add(Task::Diskpart(diskpart_script));
} }
} }
} }
@ -200,12 +181,12 @@ impl App {
popup::Type::Info, popup::Type::Info,
String::from("Running Clone Tool"), String::from("Running Clone Tool"),
))?; ))?;
self.tasks.add(TaskType::CommandWait( self.tasks.add(Task::Command(
self.config.clone_app_path.clone(), self.config.clone_app_path.clone(),
Vec::new(), Vec::new(),
)); ));
if let Some(dest_index) = self.clone.disk_index_dest { if let Some(dest_index) = self.clone.disk_index_dest {
self.tasks.add(TaskType::UpdateDestDisk(dest_index)); self.tasks.add(Task::UpdateDestDisk(dest_index));
} }
} }
Mode::PostClone => { Mode::PostClone => {
@ -215,7 +196,18 @@ impl App {
))?; ))?;
// Get System32 path // Get System32 path
let system32 = get_system32_path(&self.action_tx); let system32 = if cfg!(windows) {
if let Ok(path) = env::var("SYSTEMROOT") {
format!("{path}/System32")
} else {
self.action_tx.send(Action::Error(String::from(
"ERROR\n\n\nFailed to find SYSTEMROOT",
)))?;
return Ok(());
}
} else {
String::from(".")
};
// Add actions // Add actions
let disk_list = self.clone.disk_list.lock().unwrap(); let disk_list = self.clone.disk_list.lock().unwrap();
@ -235,7 +227,7 @@ impl App {
// Create boot files // Create boot files
for task in for task in
boot::configure_disk(&letter_boot, &letter_os, &system32, &table_type) boot::configure_disk(&letter_boot, &letter_os, &system32, table_type)
{ {
self.tasks.add(task); self.tasks.add(task);
} }
@ -256,7 +248,7 @@ impl App {
} }
Mode::Done => { Mode::Done => {
self.action_tx self.action_tx
.send(Action::DisplayPopup(popup::Type::Success, popup::fortune()))?; .send(Action::DisplayPopup(popup::Type::Success, fortune()))?;
} }
_ => {} _ => {}
} }
@ -351,9 +343,12 @@ impl App {
match action { match action {
Action::Tick => { Action::Tick => {
self.last_tick_key_events.drain(..); self.last_tick_key_events.drain(..);
// Check background task(s) match self.cur_mode {
if let Some(task) = self.tasks.poll()? { Mode::ScanDisks | Mode::PreClone | Mode::Clone | Mode::PostClone => {
self.handle_task(&task)?; // Check background task
self.tasks.poll()?; // Once all are complete Action::NextScreen is sent
}
_ => {}
} }
} }
Action::Quit => self.should_quit = true, Action::Quit => self.should_quit = true,
@ -374,9 +369,6 @@ impl App {
Mode::Confirm => { Mode::Confirm => {
self.action_tx.send(Action::NextScreen)?; self.action_tx.send(Action::NextScreen)?;
} }
Mode::Done => {
self.action_tx.send(Action::Quit)?;
}
_ => {} _ => {}
}, },
Action::Resize(w, h) => self.handle_resize(tui, w, h)?, Action::Resize(w, h) => self.handle_resize(tui, w, h)?,
@ -423,7 +415,7 @@ impl App {
0 => Some(PartitionTableType::Guid), 0 => Some(PartitionTableType::Guid),
1 => Some(PartitionTableType::Legacy), 1 => Some(PartitionTableType::Legacy),
index => { index => {
panic!("Failed to select PartitionTableType: {index}") panic!("Failed to select PartitionTableType: {}", index)
} }
} }
} else { } else {
@ -476,7 +468,6 @@ impl App {
_ => {} _ => {}
}; };
} }
Action::TasksComplete => self.action_tx.send(Action::NextScreen)?,
_ => {} _ => {}
} }
for component in &mut self.components { for component in &mut self.components {
@ -494,39 +485,6 @@ impl App {
Ok(()) Ok(())
} }
fn handle_task(&mut self, task: &Task) -> Result<()> {
match task.task_type {
TaskType::CommandWait(_, _) | TaskType::Diskpart(_) => {
// Check result
if let Some(result) = &task.result {
match result {
TaskResult::Error(msg) => {
self.action_tx
.send(Action::Error(format!("{} Failed: {msg}", task.task_type)))?;
}
TaskResult::Output(stdout, stderr, success) => {
if !success {
let msg = if !stdout.is_empty() {
stdout.clone()
} else if !stderr.is_empty() {
stderr.clone()
} else {
String::from("Unknown Error")
};
self.action_tx.send(Action::Error(format!(
"{} Failed: {msg}",
task.task_type
)))?;
}
}
}
}
}
_ => {}
}
Ok(())
}
fn render(&mut self, tui: &mut Tui) -> Result<()> { fn render(&mut self, tui: &mut Tui) -> Result<()> {
tui.draw(|frame| { tui.draw(|frame| {
if let [header, _body, footer, left, right, popup] = get_chunks(frame.area())[..] { if let [header, _body, footer, left, right, popup] = get_chunks(frame.area())[..] {
@ -608,21 +566,14 @@ fn build_footer_string(cur_mode: Mode) -> String {
String::from("(q) to quit") String::from("(q) to quit")
} }
Mode::SelectParts => String::from("(Enter) to select / (s) to start over / (q) to quit"), Mode::SelectParts => String::from("(Enter) to select / (s) to start over / (q) to quit"),
Mode::InstallDrivers => String::from("(Enter) to select / (q) to quit"),
Mode::SelectDisks => String::from( Mode::SelectDisks => String::from(
"(Enter) to select / / (i) to install driver / (r) to rescan / (q) to quit", "(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::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::Confirm => String::from("(Enter) to confirm / (b) to go back / (q) to quit"),
Mode::Done | Mode::Failed => String::from("(Enter) or (q) to quit"), Mode::Done | Mode::Failed | Mode::InstallDrivers => String::from("(Enter) or (q) to quit"),
// Invalid states // Invalid states
Mode::BootDiags Mode::PEMenu => panic!("This shouldn't happen?"),
| Mode::BootScan
| Mode::BootSetup
| Mode::DiagMenu
| Mode::InjectDrivers
| Mode::PEMenu
| Mode::SetBootMode => panic!("This shouldn't happen?"),
} }
} }
@ -687,13 +638,7 @@ fn build_left_items(app: &App, cur_mode: Mode) -> Action {
title = String::from("Done"); title = String::from("Done");
} }
// Invalid states // Invalid states
Mode::BootDiags Mode::PEMenu => panic!("This shouldn't happen?"),
| Mode::BootScan
| Mode::BootSetup
| Mode::DiagMenu
| Mode::InjectDrivers
| Mode::PEMenu
| Mode::SetBootMode => panic!("This shouldn't happen?"),
}; };
Action::UpdateLeft(title, labels, items, select_num) Action::UpdateLeft(title, labels, items, select_num)
} }
@ -746,26 +691,26 @@ fn build_right_items(app: &App, cur_mode: Mode) -> Action {
let disk_list = app.clone.disk_list.lock().unwrap(); let disk_list = app.clone.disk_list.lock().unwrap();
disk_list disk_list
.iter() .iter()
.for_each(|disk| items.push(get_disk_description_right(disk, &None))); .for_each(|disk| items.push(get_disk_description_right(&disk)));
} }
Mode::SelectParts => { Mode::SelectParts => {
for s in &["Boot", "OS"] { vec!["Boot", "OS"].iter().for_each(|s| {
labels.push(vec![DVLine { labels.push(vec![DVLine {
line_parts: vec![String::from(*s)], line_parts: vec![String::from(*s)],
line_colors: vec![Color::Cyan], line_colors: vec![Color::Cyan],
}]); }])
} });
if let Some(index) = app.clone.disk_index_dest { if let Some(index) = app.clone.disk_index_dest {
start_index = 1; start_index = 1;
let disk_list = app.clone.disk_list.lock().unwrap(); let disk_list = app.clone.disk_list.lock().unwrap();
if let Some(disk) = disk_list.get(index) { if let Some(disk) = disk_list.get(index) {
// Disk Details // Disk Details
items.push(get_disk_description_right(disk, &None)); items.push(get_disk_description_right(&disk));
// Partition Details // Partition Details
disk.parts disk.parts
.iter() .iter()
.for_each(|part| items.push(get_part_description(part))); .for_each(|part| items.push(get_part_description(&part)));
} }
} }
} }
@ -774,18 +719,18 @@ fn build_right_items(app: &App, cur_mode: Mode) -> Action {
Action::UpdateRight(labels, start_index, items) Action::UpdateRight(labels, start_index, items)
} }
pub fn get_system32_path(action_tx: &mpsc::UnboundedSender<Action>) -> String { fn fortune() -> String {
let mut system32_path = String::from("."); String::from(match random::<u8>() / 4 {
if cfg!(windows) { 0 => "FUN FACT\n\n\nComputers barely work.",
if let Ok(path) = env::var("SYSTEMROOT") { 1 => "CRASH OVERRIDE\n\n\n\"Hack the planet!\"",
system32_path = format!("{path}/System32"); 2 => "CATS\n\n\n\"All your base are belong to us!\"",
} else { 3 => "HMM\n\n\nThis has all happened before...\n\nThis will all happen again.",
action_tx 4 => "CYPHER\n\n\n\"I dont even see the code. All I see is blonde, brunette, red-head.\"",
.send(Action::Error(String::from( 5 => "CONGRATULATIONS\n\n\nYour did it!",
"ERROR\n\n\nFailed to find SYSTEMROOT", 6 => "DID YOU KNOW?\n\n\nmacOS includes a built-in screen reader!",
))) 7 => "TIP OF THE DAY\n\n\nNever go full Snappy!",
.expect("Failed to find SYSTEMROOT and then failed to send action"); 8 => "WORDS OF WISDOM\n\n\n\nIts not DNS,\n\nTheres no way its DNS,\n\nIt was DNS.",
} 9 => "HAL 9000\n\n\n\"I'm sorry Dave, I'm afraid I can't do that.\"",
} _ => "COMPLETE\n\n\nThank you for using this tool!",
system32_path })
} }

View file

@ -1,20 +1,21 @@
// This file is part of Deja-Vu. // This file is part of Deja-vu.
// //
// Deja-Vu is free software: you can redistribute it and/or modify it // 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 // under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or // the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version. // (at your option) any later version.
// //
// Deja-Vu is distributed in the hope that it will be useful, but // Deja-vu is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of // WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
// See the GNU General Public License for more details. // See the GNU General Public License for more details.
// //
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Deja-Vu. If not, see <https://www.gnu.org/licenses/>. // along with Deja-vu. If not, see <https://www.gnu.org/licenses/>.
// //
use clap::Parser; use clap::Parser;
use color_eyre::Result; use color_eyre::Result;
use core;
use crate::app::App; use crate::app::App;
@ -22,22 +23,11 @@ mod app;
#[tokio::main] #[tokio::main]
async fn main() -> Result<()> { async fn main() -> Result<()> {
let mut msg = None; core::errors::init()?;
if cfg!(windows) { core::logging::init()?;
use check_elevation::is_elevated;
if !is_elevated().expect("Failed to get elevation status.") {
msg.replace("Administrator privedges required for Deja-Vu.");
}
};
if let Some(text) = msg {
println!("{text}");
} else {
core::errors::init()?;
core::logging::init()?;
let args = core::cli::Cli::parse(); let args = core::cli::Cli::parse();
let mut app = App::new(args.tick_rate, args.frame_rate)?; let mut app = App::new(args.tick_rate, args.frame_rate)?;
app.run().await?; app.run().await?;
}
Ok(()) Ok(())
} }

View file

@ -1,5 +0,0 @@
name = 'Boot-Diagnostics'
command = 'X:\tools\boot-diags.exe'
description = "Boot issue assessment tool"
use_conemu = true
separator = false

View file

@ -1,22 +1,22 @@
# This file is part of Deja-Vu. # This file is part of Deja-vu.
# #
# Deja-Vu is free software: you can redistribute it and/or modify it # 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 # under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or # the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version. # (at your option) any later version.
# #
# Deja-Vu is distributed in the hope that it will be useful, but # Deja-vu is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of # WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the GNU General Public License for more details. # See the GNU General Public License for more details.
# #
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with Deja-Vu. If not, see <https://www.gnu.org/licenses/>. # along with Deja-vu. If not, see <https://www.gnu.org/licenses/>.
[package] [package]
name = "pe-menu" name = "pe-menu"
authors = ["2Shirt <2xShirt@gmail.com>"] authors = ["2Shirt <2xShirt@gmail.com>"]
edition = "2024" edition = "2021"
license = "GPL" license = "GPL"
version = "0.2.0" version = "0.2.0"

View file

@ -1,17 +1,17 @@
// This file is part of Deja-Vu. // This file is part of Deja-vu.
// //
// Deja-Vu is free software: you can redistribute it and/or modify it // 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 // under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or // the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version. // (at your option) any later version.
// //
// Deja-Vu is distributed in the hope that it will be useful, but // Deja-vu is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of // WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
// See the GNU General Public License for more details. // See the GNU General Public License for more details.
// //
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Deja-Vu. If not, see <https://www.gnu.org/licenses/>. // along with Deja-vu. If not, see <https://www.gnu.org/licenses/>.
// //
use anyhow::Result; use anyhow::Result;
use vergen_gix::{BuildBuilder, CargoBuilder, Emitter, GixBuilder}; use vergen_gix::{BuildBuilder, CargoBuilder, Emitter, GixBuilder};

View file

@ -1,28 +1,28 @@
// This file is part of Deja-Vu. // This file is part of Deja-vu.
// //
// Deja-Vu is free software: you can redistribute it and/or modify it // 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 // under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or // the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version. // (at your option) any later version.
// //
// Deja-Vu is distributed in the hope that it will be useful, but // Deja-vu is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of // WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
// See the GNU General Public License for more details. // See the GNU General Public License for more details.
// //
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Deja-Vu. If not, see <https://www.gnu.org/licenses/>. // along with Deja-vu. If not, see <https://www.gnu.org/licenses/>.
// //
use core::{ use core::{
action::Action, action::Action,
components::{ components::{
Component, footer::Footer, fps::FpsCounter, left::Left, popup, right::Right, footer::Footer, fps::FpsCounter, left::Left, popup, right::Right, state::StatefulList,
state::StatefulList, title::Title, title::Title, Component,
}, },
config::Config, config::Config,
line::DVLine, line::DVLine,
state::Mode, state::Mode,
tasks::{TaskType, Tasks}, tasks::{Task, Tasks},
tui::{Event, Tui}, tui::{Event, Tui},
}; };
use std::{ use std::{
@ -230,55 +230,25 @@ impl App {
// Run selected tool // Run selected tool
if let Some(tool) = self.list.get_selected() { if let Some(tool) = self.list.get_selected() {
info!("Run tool: {:?}", &tool); info!("Run tool: {:?}", &tool);
self.tasks.add(build_tool_command(self, &tool)); self.tasks.add(build_command(&self, &tool));
} }
} }
Action::Resize(w, h) => self.handle_resize(tui, w, h)?, Action::Resize(w, h) => self.handle_resize(tui, w, h)?,
Action::Render => self.render(tui)?, Action::Render => self.render(tui)?,
Action::SetMode(mode) => { Action::SetMode(mode) => {
self.mode = mode; self.mode = mode;
self.action_tx.send(Action::UpdateFooter(String::from( self.action_tx
"(Enter) to select / (t) for terminal / (p) to power off / (r) to restart", .send(Action::UpdateFooter(String::from("(Enter) to select")))?;
)))?;
self.action_tx.send(build_left_items(self))?; self.action_tx.send(build_left_items(self))?;
self.action_tx.send(build_right_items(self))?; self.action_tx.send(build_right_items(self))?;
self.action_tx.send(Action::Select(None, None))?; self.action_tx.send(Action::Select(None, None))?;
} }
Action::OpenTerminal => { Action::OpenTerminal => {
self.tasks.add(TaskType::CommandNoWait( self.tasks.add(Task::Command(
PathBuf::from("cmd.exe"), PathBuf::from("cmd.exe"),
vec![String::from("-new_console:n")], vec![String::from("-new_console:n")],
)); ));
} }
Action::Restart => {
self.action_tx.send(Action::DisplayPopup(
popup::Type::Info,
String::from("Restarting..."),
))?;
self.tasks.add(TaskType::CommandWait(
PathBuf::from("X:/Windows/System32/sync64.exe"),
vec![String::from("-r")],
));
self.tasks.add(TaskType::CommandWait(
PathBuf::from("X:/Windows/System32/wpeutil.exe"),
vec![String::from("reboot")],
));
}
Action::Shutdown => {
// NOTE: Using 'Powering off' to match the key pressed
self.action_tx.send(Action::DisplayPopup(
popup::Type::Info,
String::from("Powering off..."),
))?;
self.tasks.add(TaskType::CommandWait(
PathBuf::from("X:/Windows/System32/sync64.exe"),
vec![String::from("-r")],
));
self.tasks.add(TaskType::CommandWait(
PathBuf::from("X:/Windows/System32/wpeutil.exe"),
vec![String::from("shutdown")],
));
}
_ => {} _ => {}
} }
for component in &mut self.components { for component in &mut self.components {
@ -371,15 +341,14 @@ fn get_chunks(r: Rect) -> Vec<Rect> {
chunks chunks
} }
pub fn build_tool_command(app: &App, tool: &Tool) -> TaskType { pub fn build_command(app: &App, tool: &Tool) -> Task {
let cmd_path: PathBuf; let cmd_path: PathBuf;
let mut cmd_args: Vec<String> = Vec::new(); let mut cmd_args: Vec<String> = Vec::new();
let start_index: usize; let start_index: usize;
if tool.use_conemu { if tool.use_conemu {
cmd_path = app.config.conemu_path.clone(); cmd_path = app.config.conemu_path.clone();
cmd_args.push(String::from("-run"));
cmd_args.push(tool.command.clone());
cmd_args.push(String::from("-new_console:n")); cmd_args.push(String::from("-new_console:n"));
cmd_args.push(tool.command.clone());
start_index = 1; start_index = 1;
} else { } else {
cmd_path = PathBuf::from(tool.command.clone()); cmd_path = PathBuf::from(tool.command.clone());
@ -392,7 +361,7 @@ pub fn build_tool_command(app: &App, tool: &Tool) -> TaskType {
}); });
} }
} }
TaskType::CommandNoWait(cmd_path, cmd_args) Task::Command(cmd_path, cmd_args)
} }
fn build_left_items(app: &App) -> Action { fn build_left_items(app: &App) -> Action {
@ -453,7 +422,7 @@ pub fn load_tools() -> Vec<Tool> {
entries entries
.iter() .iter()
.map(|entry| { .map(|entry| {
let contents = fs::read_to_string(entry).expect("Failed to read tool config file"); let contents = fs::read_to_string(&entry).expect("Failed to read tool config file");
let tool: Tool = toml::from_str(&contents).expect("Failed to parse tool config file"); let tool: Tool = toml::from_str(&contents).expect("Failed to parse tool config file");
tool tool
}) })

View file

@ -1,20 +1,21 @@
// This file is part of Deja-Vu. // This file is part of Deja-vu.
// //
// Deja-Vu is free software: you can redistribute it and/or modify it // 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 // under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or // the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version. // (at your option) any later version.
// //
// Deja-Vu is distributed in the hope that it will be useful, but // Deja-vu is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of // WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
// See the GNU General Public License for more details. // See the GNU General Public License for more details.
// //
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Deja-Vu. If not, see <https://www.gnu.org/licenses/>. // along with Deja-vu. If not, see <https://www.gnu.org/licenses/>.
// //
use clap::Parser; use clap::Parser;
use color_eyre::Result; use color_eyre::Result;
use core;
use crate::app::App; use crate::app::App;