Show boot/OS partitions during SelectParts

This commit is contained in:
2Shirt 2024-11-14 05:50:30 -08:00
parent ff344d4e37
commit c9884d5b6d
Signed by: 2Shirt
GPG key ID: 152FAC923B0E132C
5 changed files with 212 additions and 67 deletions

View file

@ -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<Mutex<Vec<Disk>>>,
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]);
})
}

View file

@ -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)

View file

@ -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}"))));
}
}
}
}
_ => {}
}

View file

@ -31,15 +31,7 @@ impl<T: Clone> StatefulList<T> {
self.items.clear();
}
pub fn get(&mut self, index: usize) -> Option<T> {
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)
}

View file

@ -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<Disk> {
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<Disk> {
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