Show source/destination disk info

This commit is contained in:
2Shirt 2024-11-12 23:23:07 -08:00
parent 2a5167bf52
commit 6c76021bf4
Signed by: 2Shirt
GPG key ID: 152FAC923B0E132C
3 changed files with 178 additions and 49 deletions

View file

@ -18,17 +18,25 @@ use crossterm::event::KeyEvent;
use ratatui::{prelude::*, widgets::*}; use ratatui::{prelude::*, widgets::*};
use tokio::sync::mpsc::UnboundedSender; use tokio::sync::mpsc::UnboundedSender;
use super::Component; use super::{state::StatefulList, Component};
use crate::{action::Action, app::Mode, config::Config, system::disk::Disk}; use crate::{
action::Action,
app::Mode,
config::Config,
system::{
disk::{Disk, Partition, PartitionTableType},
},
};
#[derive(Default)] #[derive(Default)]
pub struct Right { pub struct Right {
command_tx: Option<UnboundedSender<Action>>, command_tx: Option<UnboundedSender<Action>>,
config: Config, config: Config,
cur_mode: Mode, cur_mode: Mode,
disks: Vec<Disk>, list_disks: StatefulList<Disk>,
list_parts: StatefulList<Partition>,
table_type: Option<PartitionTableType>,
prev_mode: Mode, prev_mode: Mode,
all_modes: Vec<Mode>,
selections: Vec<Option<usize>>, selections: Vec<Option<usize>>,
} }
@ -65,18 +73,26 @@ impl Component for Right {
Action::Render => { Action::Render => {
// add any logic here that should run on every render // add any logic here that should run on every render
} }
Action::KeyUp => match self.cur_mode {
Mode::SelectDisks => self.list_disks.previous(),
Mode::SelectParts => self.list_parts.previous(),
_ => {}
},
Action::KeyDown => match self.cur_mode {
Mode::SelectDisks => self.list_disks.next(),
Mode::SelectParts => self.list_parts.next(),
_ => {}
},
Action::Select(one, two) => { Action::Select(one, two) => {
self.selections[0] = one; self.selections[0] = one;
self.selections[1] = two; self.selections[1] = two;
} }
Action::SelectTableType(table_type) => self.table_type = Some(table_type),
Action::SetMode(new_mode) => { Action::SetMode(new_mode) => {
if self.cur_mode != Mode::Confirm { self.prev_mode = self.cur_mode;
self.prev_mode = self.cur_mode;
}
self.cur_mode = new_mode; self.cur_mode = new_mode;
self.all_modes.push(new_mode);
} }
Action::UpdateDiskList(disks) => self.disks = disks, Action::UpdateDiskList(disks) => self.list_disks.set_items(disks),
_ => {} _ => {}
} }
Ok(None) Ok(None)
@ -95,20 +111,131 @@ impl Component for Right {
frame.render_widget(title, title_area); frame.render_widget(title, title_area);
// Body // Body
let paragraph = Paragraph::new(format!( let mut body_text = Vec::new();
"Prev Mode: {:?} // Cur Mode: {:?}\n{:?}\n{:?}\nDisks: {}", match (self.prev_mode, self.cur_mode) {
self.prev_mode, (_, Mode::InstallDrivers) => {
self.cur_mode, }
self.all_modes, (_, Mode::SelectDisks) | (Mode::SelectDisks | Mode::SelectTableType, Mode::Confirm) => {
self.selections, // Source Disk
self.disks.len(), body_text.push(Line::from(Span::styled(
)) "Source:",
.block( Style::default().cyan().bold(),
Block::default() )));
.borders(Borders::ALL) body_text.push(Line::from(""));
.padding(Padding::new(1, 1, 1, 1)), body_text.push(Line::from(Span::styled(
); format!(
frame.render_widget(paragraph, body_area); "{:<8} {:>11} {:<4} {:<4} {}",
"Disk ID", "Size", "Conn", "Type", "Model (Serial)"
),
Style::new().green().bold(),
)));
let index = if self.selections[0].is_some() {
self.selections[0]
} else {
self.list_disks.selected()
};
if let Some(i) = index {
if let Some(disk) = self.list_disks.get_ref(i) {
body_text.push(Line::from(Span::raw(&disk.description)));
// Source parts
body_text.push(Line::from(""));
body_text.push(Line::from(Span::styled(
format!(
"{:<8} {:>11} {:<7} {}",
"Part ID", "Size", "(FS)", "\"Label\""
),
Style::new().blue().bold(),
)));
for line in &disk.parts_description {
body_text.push(Line::from(Span::raw(line)));
}
}
}
// Destination Disk
let index = match (self.selections[0], self.selections[1]) {
(Some(one), None) => {
// First selected
if let Some(two) = self.selections[1] {
if one != two {
self.selections[1]
} else {
None
}
} else {
self.list_disks.selected()
}
}
(Some(_), Some(_)) => {
// Both selected
self.selections[1]
}
(_, _) => None,
};
if let Some(i) = index {
// Divider
body_text.push(Line::from(""));
body_text.push(Line::from(str::repeat("", (body_area.width - 4) as usize)));
body_text.push(Line::from(""));
// Disk
if let Some(disk) = self.list_disks.get_ref(i) {
body_text.push(Line::from(vec![
Span::styled("Dest:", Style::default().cyan().bold()),
Span::styled(
" (WARNING: ALL DATA WILL BE DELETED!)",
Style::default().red().bold(),
),
]));
if let Some(table_type) = &self.table_type {
body_text.push(Line::from(Span::styled(
format!(" (Will be formatted {})", table_type),
Style::default().yellow().bold(),
)));
}
body_text.push(Line::from(""));
body_text.push(Line::from(Span::styled(
format!(
"{:<8} {:>11} {:<4} {:<4} {}",
"Disk ID", "Size", "Conn", "Type", "Model (Serial)"
),
Style::new().green().bold(),
)));
body_text.push(Line::from(Span::raw(&disk.description)));
// Destination parts
body_text.push(Line::from(""));
body_text.push(Line::from(Span::styled(
format!(
"{:<8} {:>11} {:<7} {}",
"Part ID", "Size", "(FS)", "\"Label\""
),
Style::new().blue().bold(),
)));
for line in &disk.parts_description {
body_text.push(Line::from(Span::raw(line)));
}
}
}
}
(_, Mode::SelectTableType) => {
//
}
(_, Mode::SelectParts) | (Mode::SelectParts, Mode::Confirm) => {
//
}
_ => {}
}
let body = Paragraph::new(body_text)
.style(Style::default().fg(Color::Gray))
.wrap(Wrap { trim: false })
.block(
Block::default()
.borders(Borders::ALL)
.padding(Padding::new(1, 1, 1, 1)),
);
frame.render_widget(body, body_area);
// Done // Done
Ok(()) Ok(())

View file

@ -39,6 +39,10 @@ impl<T: Clone> StatefulList<T> {
} }
} }
pub fn get_ref(&self, index: usize) -> Option<&T> {
self.items.get(index)
}
pub fn get_selected(&self) -> Option<T> { pub fn get_selected(&self) -> Option<T> {
if let Some(i) = self.state.selected() { if let Some(i) = self.state.selected() {
Some(self.items[i].clone()) Some(self.items[i].clone())

View file

@ -30,6 +30,7 @@ use crate::system::diskpart;
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] #[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
pub struct Disk { pub struct Disk {
pub conn_type: String, pub conn_type: String,
pub description: String,
pub id: usize, pub id: usize,
pub model: String, pub model: String,
pub part_type: PartitionTableType, pub part_type: PartitionTableType,
@ -60,6 +61,7 @@ pub enum PartitionTableType {
impl Disk { impl Disk {
pub fn generate_descriptions(&mut self) { pub fn generate_descriptions(&mut self) {
self.description = format!("{self}");
for part in &self.parts { for part in &self.parts {
self.parts_description.push(format!("{part}")); self.parts_description.push(format!("{part}"));
} }
@ -85,27 +87,19 @@ impl Disk {
impl fmt::Display for Disk { impl fmt::Display for Disk {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if cfg!(windows) { write!(
write!( f,
f, "Disk {:<3} {:>11} {:<4} {:<4} {} ({})",
"Disk {:<3} {:>11} {:<4} {} ({})", self.id,
self.id, bytes_to_string(&self.size),
bytes_to_string(&self.size), self.conn_type,
self.conn_type, match self.part_type {
self.model, PartitionTableType::Guid => "GPT",
self.serial, PartitionTableType::Legacy => "MBR",
) },
} else { self.model,
write!( self.serial,
f, )
"{:<14} {:>11} {:<4} {} ({})",
self.id,
bytes_to_string(&self.size),
self.conn_type,
self.model,
self.serial,
)
}
} }
} }
@ -167,7 +161,7 @@ pub fn get_fake_disks() -> Vec<Disk> {
let mut disks = vec![ let mut disks = vec![
Disk { Disk {
conn_type: "SATA".to_string(), conn_type: "SATA".to_string(),
id: 1, id: 0,
model: "Samsung Evo 870".to_string(), model: "Samsung Evo 870".to_string(),
part_type: PartitionTableType::Legacy, part_type: PartitionTableType::Legacy,
parts: vec![ parts: vec![
@ -208,7 +202,7 @@ pub fn get_fake_disks() -> Vec<Disk> {
}, },
Disk { Disk {
conn_type: "SATA".to_string(), conn_type: "SATA".to_string(),
id: 2, id: 1,
model: "ADATA Garbage".to_string(), model: "ADATA Garbage".to_string(),
part_type: PartitionTableType::Legacy, part_type: PartitionTableType::Legacy,
parts: vec![Partition { parts: vec![Partition {
@ -225,7 +219,7 @@ pub fn get_fake_disks() -> Vec<Disk> {
}, },
Disk { Disk {
conn_type: "NVMe".to_string(), conn_type: "NVMe".to_string(),
id: 3, id: 2,
model: "Crucial P3 Plus".to_string(), model: "Crucial P3 Plus".to_string(),
part_type: PartitionTableType::Guid, part_type: PartitionTableType::Guid,
parts: vec![ parts: vec![
@ -260,7 +254,7 @@ pub fn get_fake_disks() -> Vec<Disk> {
}, },
Disk { Disk {
conn_type: "IDE".to_string(), conn_type: "IDE".to_string(),
id: 4, id: 3,
model: "Fireball".to_string(), model: "Fireball".to_string(),
part_type: PartitionTableType::Guid, part_type: PartitionTableType::Guid,
parts: vec![ parts: vec![
@ -285,13 +279,17 @@ pub fn get_fake_disks() -> Vec<Disk> {
}, },
Disk { Disk {
conn_type: "MISC".to_string(), conn_type: "MISC".to_string(),
id: 5, id: 4,
part_type: PartitionTableType::Legacy, part_type: PartitionTableType::Legacy,
model: "Iomega".to_string(), model: "Iomega".to_string(),
serial: "000".to_string(), serial: "000".to_string(),
size: 14, size: 14,
..Default::default() ..Default::default()
}, },
Disk {
id: 5,
..Default::default()
},
Disk::default(), Disk::default(),
]; ];
for disk in &mut disks { for disk in &mut disks {