Show boot/OS partitions during SelectParts
This commit is contained in:
parent
ff344d4e37
commit
c9884d5b6d
5 changed files with 212 additions and 67 deletions
17
src/app.rs
17
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<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]);
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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}"))));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in a new issue