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,
|
config::Config,
|
||||||
system::{
|
system::{
|
||||||
disk::{get_disks, Disk, PartitionTableType},
|
disk::{get_disks, refresh_disk_info, Disk, PartitionTableType},
|
||||||
diskpart::{build_dest_format_script, refresh_disk_info},
|
diskpart::build_dest_format_script,
|
||||||
drivers::{self, Driver},
|
drivers::{self, Driver},
|
||||||
},
|
},
|
||||||
tui::{Event, Tui},
|
tui::{Event, Tui},
|
||||||
|
|
@ -365,6 +365,7 @@ impl App {
|
||||||
self.config.clone_app_path.clone(),
|
self.config.clone_app_path.clone(),
|
||||||
Vec::new(),
|
Vec::new(),
|
||||||
))?;
|
))?;
|
||||||
|
self.action_tx.send(Action::UpdateDestDisk)?;
|
||||||
}
|
}
|
||||||
Mode::PostClone => {
|
Mode::PostClone => {
|
||||||
self.action_tx.send(Action::DisplayPopup(
|
self.action_tx.send(Action::DisplayPopup(
|
||||||
|
|
@ -618,12 +619,8 @@ fn lazy_update_dest_disk(
|
||||||
disk_list_arc: Arc<Mutex<Vec<Disk>>>,
|
disk_list_arc: Arc<Mutex<Vec<Disk>>>,
|
||||||
dest_index: usize,
|
dest_index: usize,
|
||||||
) -> JoinHandle<()> {
|
) -> JoinHandle<()> {
|
||||||
if cfg!(windows) {
|
thread::spawn(move || {
|
||||||
thread::spawn(move || {
|
let mut disks = disk_list_arc.lock().unwrap();
|
||||||
let mut disks = disk_list_arc.lock().unwrap();
|
refresh_disk_info(&mut disks[dest_index]);
|
||||||
refresh_disk_info(&mut disks[dest_index]);
|
})
|
||||||
})
|
|
||||||
} else {
|
|
||||||
thread::spawn(|| sleep(Duration::from_secs(2)))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -214,31 +214,6 @@ impl Component for Left {
|
||||||
}
|
}
|
||||||
(_, Mode::SelectParts) => {
|
(_, Mode::SelectParts) => {
|
||||||
self.title_text = String::from("Select Boot and OS Partitions");
|
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) => {
|
(Mode::SelectDisks | Mode::SelectParts, Mode::Confirm) => {
|
||||||
self.title_text = String::from("Confirm Selections")
|
self.title_text = String::from("Confirm Selections")
|
||||||
|
|
@ -251,7 +226,34 @@ impl Component for Left {
|
||||||
(_, Mode::Confirm) => panic!("This shouldn't happen."),
|
(_, 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)
|
Ok(None)
|
||||||
|
|
|
||||||
|
|
@ -103,6 +103,8 @@ impl Component for Right {
|
||||||
Mode::SelectDisks => {
|
Mode::SelectDisks => {
|
||||||
self.selections[0] = None;
|
self.selections[0] = None;
|
||||||
self.selections[1] = None;
|
self.selections[1] = None;
|
||||||
|
self.selected_disks[0] = None;
|
||||||
|
self.selected_disks[1] = None;
|
||||||
}
|
}
|
||||||
Mode::SelectParts => {
|
Mode::SelectParts => {
|
||||||
self.selections[0] = None;
|
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)
|
Ok(None)
|
||||||
|
|
@ -167,7 +196,7 @@ impl Component for Right {
|
||||||
self.list_disks.selected()
|
self.list_disks.selected()
|
||||||
};
|
};
|
||||||
if let Some(i) = index {
|
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)));
|
body_text.push(Line::from(Span::raw(&disk.description)));
|
||||||
|
|
||||||
// Source parts
|
// Source parts
|
||||||
|
|
@ -218,7 +247,7 @@ impl Component for Right {
|
||||||
body_text.push(Line::from(""));
|
body_text.push(Line::from(""));
|
||||||
|
|
||||||
// Disk
|
// 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![
|
body_text.push(Line::from(vec![
|
||||||
Span::styled("Dest:", Style::default().cyan().bold()),
|
Span::styled("Dest:", Style::default().cyan().bold()),
|
||||||
Span::styled(
|
Span::styled(
|
||||||
|
|
@ -258,7 +287,99 @@ impl Component for Right {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(_, Mode::SelectParts) | (Mode::SelectParts, Mode::Confirm) => {
|
(_, 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();
|
self.items.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get(&mut self, index: usize) -> Option<T> {
|
pub fn get(&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> {
|
|
||||||
self.items.get(index)
|
self.items.get(index)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -44,7 +44,6 @@ pub struct Disk {
|
||||||
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
pub struct Partition {
|
pub struct Partition {
|
||||||
pub id: usize,
|
pub id: usize,
|
||||||
pub is_selected: bool,
|
|
||||||
pub fs_type: String,
|
pub fs_type: String,
|
||||||
pub label: String,
|
pub label: String,
|
||||||
pub letter: String,
|
pub letter: String,
|
||||||
|
|
@ -62,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}");
|
self.description = format!("{self}");
|
||||||
|
self.parts_description = Vec::new();
|
||||||
for part in &self.parts {
|
for part in &self.parts {
|
||||||
self.parts_description.push(format!("{part}"));
|
self.parts_description.push(format!("{part}"));
|
||||||
}
|
}
|
||||||
|
|
@ -111,21 +111,12 @@ impl fmt::Display for Partition {
|
||||||
} else {
|
} else {
|
||||||
format!("({})", self.fs_type)
|
format!("({})", self.fs_type)
|
||||||
};
|
};
|
||||||
if cfg!(windows) {
|
s = format!(
|
||||||
s = format!(
|
"{:<8} {:>11} {:<7}",
|
||||||
"{:<8} {:>11} {:<7}",
|
self.id,
|
||||||
self.id,
|
bytes_to_string(&self.size),
|
||||||
bytes_to_string(&self.size),
|
fs
|
||||||
fs
|
);
|
||||||
);
|
|
||||||
} else {
|
|
||||||
s = format!(
|
|
||||||
"{:<14} {:>11} {:<7}",
|
|
||||||
self.id,
|
|
||||||
bytes_to_string(&self.size),
|
|
||||||
fs
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if !self.label.is_empty() {
|
if !self.label.is_empty() {
|
||||||
s = format!("{s} \"{}\"", &self.label);
|
s = format!("{s} \"{}\"", &self.label);
|
||||||
}
|
}
|
||||||
|
|
@ -227,7 +218,7 @@ pub fn get_fake_disks() -> Vec<Disk> {
|
||||||
id: 1,
|
id: 1,
|
||||||
fs_type: String::from("FAT32"),
|
fs_type: String::from("FAT32"),
|
||||||
label: String::from("ESP"),
|
label: String::from("ESP"),
|
||||||
letter: String::from("S"),
|
letter: String::from("Q"),
|
||||||
part_type: String::from("EFI"),
|
part_type: String::from("EFI"),
|
||||||
size: 272_629_760,
|
size: 272_629_760,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
|
|
@ -242,7 +233,7 @@ pub fn get_fake_disks() -> Vec<Disk> {
|
||||||
id: 4,
|
id: 4,
|
||||||
fs_type: String::from("NTFS"),
|
fs_type: String::from("NTFS"),
|
||||||
label: String::from("Win10"),
|
label: String::from("Win10"),
|
||||||
letter: String::from("W"),
|
letter: String::from("V"),
|
||||||
part_type: String::from("MS Basic Data"),
|
part_type: String::from("MS Basic Data"),
|
||||||
size: 824_340_119_552,
|
size: 824_340_119_552,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
|
|
@ -323,6 +314,48 @@ pub fn get_disk_serial_number(id: usize) -> String {
|
||||||
serial
|
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
|
/// Misc
|
||||||
///
|
///
|
||||||
/// Clippy exception is fine because this supports sizes up to 2 EiB
|
/// Clippy exception is fine because this supports sizes up to 2 EiB
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue