From c9884d5b6d0bb7d30023a749a562fbddd28483cd Mon Sep 17 00:00:00 2001 From: 2Shirt <2xShirt@gmail.com> Date: Thu, 14 Nov 2024 05:50:30 -0800 Subject: [PATCH] Show boot/OS partitions during SelectParts --- src/app.rs | 17 +++--- src/components/left.rs | 54 +++++++++-------- src/components/right.rs | 129 ++++++++++++++++++++++++++++++++++++++-- src/components/state.rs | 10 +--- src/system/disk.rs | 69 +++++++++++++++------ 5 files changed, 212 insertions(+), 67 deletions(-) diff --git a/src/app.rs b/src/app.rs index 9650dd2..3f1e058 100644 --- a/src/app.rs +++ b/src/app.rs @@ -39,8 +39,8 @@ use crate::{ }, config::Config, system::{ - disk::{get_disks, Disk, PartitionTableType}, - diskpart::{build_dest_format_script, refresh_disk_info}, + disk::{get_disks, refresh_disk_info, Disk, PartitionTableType}, + diskpart::build_dest_format_script, drivers::{self, Driver}, }, tui::{Event, Tui}, @@ -365,6 +365,7 @@ impl App { self.config.clone_app_path.clone(), Vec::new(), ))?; + self.action_tx.send(Action::UpdateDestDisk)?; } Mode::PostClone => { self.action_tx.send(Action::DisplayPopup( @@ -618,12 +619,8 @@ fn lazy_update_dest_disk( disk_list_arc: Arc>>, dest_index: usize, ) -> JoinHandle<()> { - if cfg!(windows) { - thread::spawn(move || { - let mut disks = disk_list_arc.lock().unwrap(); - refresh_disk_info(&mut disks[dest_index]); - }) - } else { - thread::spawn(|| sleep(Duration::from_secs(2))) - } + thread::spawn(move || { + let mut disks = disk_list_arc.lock().unwrap(); + refresh_disk_info(&mut disks[dest_index]); + }) } diff --git a/src/components/left.rs b/src/components/left.rs index 43fca3a..0bde6b1 100644 --- a/src/components/left.rs +++ b/src/components/left.rs @@ -214,31 +214,6 @@ impl Component for Left { } (_, Mode::SelectParts) => { self.title_text = String::from("Select Boot and OS Partitions"); - - // Get list of partitions for destination disk - if let Some(index) = &self.disk_id_dest { - if let Some(disk) = self.list_disks.get(*index).to_owned() { - self.list_parts.set_items(disk.get_parts()); - - // Auto-select first partition and highlight likely OS partition - if let Some(table_type) = &self.table_type { - match table_type { - PartitionTableType::Guid => { - if disk.num_parts() >= 3 { - self.selections[0] = Some(0); - self.list_parts.select(2); - } - } - PartitionTableType::Legacy => { - if disk.num_parts() >= 2 { - self.selections[0] = Some(0); - self.list_parts.select(1); - } - } - } - } - } - } } (Mode::SelectDisks | Mode::SelectParts, Mode::Confirm) => { self.title_text = String::from("Confirm Selections") @@ -251,7 +226,34 @@ impl Component for Left { (_, Mode::Confirm) => panic!("This shouldn't happen."), } } - Action::UpdateDiskList(disks) => self.list_disks.set_items(disks), + Action::UpdateDiskList(disks) => { + self.list_disks.set_items(disks); + if self.mode == Mode::SelectParts { + if let Some(index) = self.disk_id_dest { + if let Some(disk) = self.list_disks.get(index) { + self.list_parts.set_items(disk.get_parts()); + + // Auto-select first partition and highlight likely OS partition + if let Some(table_type) = &self.table_type { + match table_type { + PartitionTableType::Guid => { + if disk.num_parts() >= 3 { + self.selections[0] = Some(0); + self.list_parts.select(2); + } + } + PartitionTableType::Legacy => { + if disk.num_parts() >= 2 { + self.selections[0] = Some(0); + self.list_parts.select(1); + } + } + } + } + } + } + } + } _ => {} } Ok(None) diff --git a/src/components/right.rs b/src/components/right.rs index f9f2587..051f73b 100644 --- a/src/components/right.rs +++ b/src/components/right.rs @@ -103,6 +103,8 @@ impl Component for Right { Mode::SelectDisks => { self.selections[0] = None; self.selections[1] = None; + self.selected_disks[0] = None; + self.selected_disks[1] = None; } Mode::SelectParts => { self.selections[0] = None; @@ -116,7 +118,34 @@ impl Component for Right { _ => {} } } - Action::UpdateDiskList(disks) => self.list_disks.set_items(disks), + Action::UpdateDiskList(disks) => { + self.list_disks.set_items(disks); + if self.cur_mode == Mode::SelectParts { + if let Some(index) = self.selected_disks[1] { + if let Some(disk) = self.list_disks.get(index) { + self.list_parts.set_items(disk.get_parts()); + + // Auto-select first partition and highlight likely OS partition + if let Some(table_type) = &self.table_type { + match table_type { + PartitionTableType::Guid => { + if disk.num_parts() >= 3 { + self.selections[0] = Some(0); + self.list_parts.select(2); + } + } + PartitionTableType::Legacy => { + if disk.num_parts() >= 2 { + self.selections[0] = Some(0); + self.list_parts.select(1); + } + } + } + } + } + } + } + } _ => {} } Ok(None) @@ -167,7 +196,7 @@ impl Component for Right { self.list_disks.selected() }; if let Some(i) = index { - if let Some(disk) = self.list_disks.get_ref(i) { + if let Some(disk) = self.list_disks.get(i) { body_text.push(Line::from(Span::raw(&disk.description))); // Source parts @@ -218,7 +247,7 @@ impl Component for Right { body_text.push(Line::from("")); // Disk - if let Some(disk) = self.list_disks.get_ref(i) { + if let Some(disk) = self.list_disks.get(i) { body_text.push(Line::from(vec![ Span::styled("Dest:", Style::default().cyan().bold()), Span::styled( @@ -258,7 +287,99 @@ impl Component for Right { } } (_, Mode::SelectParts) | (Mode::SelectParts, Mode::Confirm) => { - // + // Disk + if let Some(index) = self.selected_disks[1] { + if let Some(disk) = self.list_disks.get(index) { + body_text.push(Line::from(Span::styled( + "Dest:", + Style::default().cyan().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))); + } + } + + // Divider + body_text.push(Line::from("")); + body_text.push(Line::from(str::repeat("─", (body_area.width - 4) as usize))); + body_text.push(Line::from("")); + + // Boot Partition + // i.e. either the previously selected part or the highlighted one (if possible) + let mut boot_index = self.selections[0]; + if boot_index.is_none() { + if let Some(i) = self.list_parts.selected() { + boot_index = Some(i); + } + } + if let Some(i) = boot_index { + if let Some(part) = self.list_parts.get(i) { + body_text.push(Line::from(Span::styled( + "Boot:", + Style::default().cyan().bold(), + ))); + 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(), + ))); + body_text.push(Line::from(Span::raw(format!("{part}")))); + } + } + + // OS Partition + // i.e. either the previously selected part or the highlighted one (if needed) + let mut os_index = self.selections[1]; + if os_index.is_none() { + if let Some(boot_index) = self.selections[0] { + if let Some(i) = self.list_parts.selected() { + if boot_index != i { + os_index = Some(i); + } + } + } + } + if let Some(i) = os_index { + if let Some(part) = self.list_parts.get(i) { + body_text.push(Line::from("")); + body_text.push(Line::from(Span::styled( + "OS:", + Style::default().cyan().bold(), + ))); + 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(), + ))); + body_text.push(Line::from(Span::raw(format!("{part}")))); + } + } + } } _ => {} } diff --git a/src/components/state.rs b/src/components/state.rs index 24cc056..7a4689d 100644 --- a/src/components/state.rs +++ b/src/components/state.rs @@ -31,15 +31,7 @@ impl StatefulList { self.items.clear(); } - pub fn get(&mut self, index: usize) -> Option { - if let Some(item) = self.items.get(index) { - Some(item.clone()) - } else { - None - } - } - - pub fn get_ref(&self, index: usize) -> Option<&T> { + pub fn get(&self, index: usize) -> Option<&T> { self.items.get(index) } diff --git a/src/system/disk.rs b/src/system/disk.rs index 6dc005c..29d44b2 100644 --- a/src/system/disk.rs +++ b/src/system/disk.rs @@ -44,7 +44,6 @@ pub struct Disk { #[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)] pub struct Partition { pub id: usize, - pub is_selected: bool, pub fs_type: String, pub label: String, pub letter: String, @@ -62,6 +61,7 @@ pub enum PartitionTableType { impl Disk { pub fn generate_descriptions(&mut self) { self.description = format!("{self}"); + self.parts_description = Vec::new(); for part in &self.parts { self.parts_description.push(format!("{part}")); } @@ -111,21 +111,12 @@ impl fmt::Display for Partition { } else { format!("({})", self.fs_type) }; - if cfg!(windows) { - s = format!( - "{:<8} {:>11} {:<7}", - self.id, - bytes_to_string(&self.size), - fs - ); - } else { - s = format!( - "{:<14} {:>11} {:<7}", - self.id, - bytes_to_string(&self.size), - fs - ); - } + s = format!( + "{:<8} {:>11} {:<7}", + self.id, + bytes_to_string(&self.size), + fs + ); if !self.label.is_empty() { s = format!("{s} \"{}\"", &self.label); } @@ -227,7 +218,7 @@ pub fn get_fake_disks() -> Vec { id: 1, fs_type: String::from("FAT32"), label: String::from("ESP"), - letter: String::from("S"), + letter: String::from("Q"), part_type: String::from("EFI"), size: 272_629_760, ..Default::default() @@ -242,7 +233,7 @@ pub fn get_fake_disks() -> Vec { id: 4, fs_type: String::from("NTFS"), label: String::from("Win10"), - letter: String::from("W"), + letter: String::from("V"), part_type: String::from("MS Basic Data"), size: 824_340_119_552, ..Default::default() @@ -323,6 +314,48 @@ pub fn get_disk_serial_number(id: usize) -> String { serial } +pub fn refresh_disk_info(disk: &mut Disk) { + if cfg!(windows) { + info!("Refresh disk via Diskpart"); + diskpart::refresh_disk_info(disk); + } else { + info!("Refresh fake disk"); + refresh_fake_disk_info(disk); + sleep(Duration::from_millis(500)); + } +} + +fn refresh_fake_disk_info(disk: &mut Disk) { + disk.parts = vec![ + Partition { + id: 1, + fs_type: String::from("FAT32"), + label: String::from("New ESP Volume"), + letter: String::from("S"), + part_type: String::from("EFI"), + size: 272_629_760, + ..Default::default() + }, + Partition { + id: 2, + label: String::from("New MSR Partition"), + part_type: String::from("MSR"), + size: 16_777_216, + ..Default::default() + }, + Partition { + id: 4, + fs_type: String::from("NTFS"), + label: String::from("Cloned OS Volume"), + letter: String::from("W"), + part_type: String::from("MS Basic Data"), + size: 824_340_119_552, + ..Default::default() + }, + ]; + disk.generate_descriptions(); +} + /// Misc /// /// Clippy exception is fine because this supports sizes up to 2 EiB