More work on partition stuff #2, added BlkSize struct to handle partition sizes nicely.

This commit is contained in:
2026-05-23 14:56:21 +02:00
parent c6e7fa6069
commit 9743b010d1
5 changed files with 332 additions and 137 deletions
+7 -2
View File
@@ -9,6 +9,7 @@ pub fn color_bar<Message: 'static>(
let mut c_b = Row::new().height(bar_height);
if let Some(items) = maybe_items {
for (bar_text, bar_fill, bar_color) in items {
c_b = c_b.push(
container(text(bar_text).color(Color::BLACK).size(iced::Pixels(14.0)))
@@ -16,9 +17,13 @@ pub fn color_bar<Message: 'static>(
.width(Length::FillPortion(bar_fill))
.style(move |_| container::background(bar_color))
.align_x(Alignment::Center)
.align_y(Alignment::Center),
.align_y(Alignment::Center)
.padding(2),
);
}
}
c_b.into()
container(c_b.spacing(2))
.style(container::rounded_box)
.padding(3)
.into()
}
+168 -25
View File
@@ -1,4 +1,5 @@
use std::collections::HashMap;
use std::ops::{Add, Sub};
pub fn format_bytes_size(size: u64) -> String {
let suffixs: [&str; 5] = ["KB", "MB", "GB", "TB", "PB"];
@@ -19,35 +20,124 @@ pub fn format_bytes_size(size: u64) -> String {
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct BlkSize {
size_bytes: u64,
}
impl std::fmt::Display for BlkSize {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if self.size_bytes < 1000 {
write!(f, "{}B", self.size_bytes)
} else {
let mut s = self.size_bytes as f64;
let mut idx = 0;
for i in 0..5 {
s = s / 1024.0;
idx = i;
if s < 1000.0 {
break;
}
}
write!(f, "{:.2}{}", s, Self::SUFFIXS[idx])
}
}
}
impl BlkSize {
pub const SUFFIXS: [&str; 5] = ["KB", "MB", "GB", "TB", "PB"];
pub fn new(size:u64) -> Self {
Self { size_bytes: size }
}
pub fn new_kb(size:u64) -> Self {
Self { size_bytes: size * 1024 }
}
pub fn new_mb(size:u64) -> Self {
Self { size_bytes: size * 1024u64.pow(2) }
}
pub fn new_gb(size:u64) -> Self {
Self { size_bytes: size * 1024u64.pow(3) }
}
pub fn new_tb(size:u64) -> Self {
Self { size_bytes: size * 1024u64.pow(4) }
}
pub fn new_pb(size:u64) -> Self {
Self { size_bytes: size * 1024u64.pow(5) }
}
pub fn b(&self) -> u64 {
self.size_bytes
}
pub fn kb(&self) -> u64 {
self.size_bytes / 1024u64.pow(2)
}
pub fn mb(&self) -> u64 {
self.size_bytes / 1024u64.pow(3)
}
pub fn gb(&self) -> u64 {
self.size_bytes / 1024u64.pow(4)
}
pub fn tb(&self) -> u64 {
self.size_bytes / 1024u64.pow(5)
}
pub fn pb(&self) -> u64 {
self.size_bytes / 1024u64.pow(6)
}
pub fn from_str_bytes(s: &str) -> Option<Self> {
Some(Self::new(u64::from_str_radix(s, 10).ok()?))
}
}
impl Add for BlkSize {
type Output = BlkSize;
fn add(self, other: BlkSize) -> BlkSize {
BlkSize {
size_bytes: self.size_bytes + other.size_bytes,
}
}
}
impl Sub for BlkSize {
type Output = BlkSize;
fn sub(self, other: BlkSize) -> BlkSize {
BlkSize {
size_bytes: self.size_bytes.strict_sub(other.size_bytes),
}
}
}
///
/// size - device size in bytes
/// sector size - default 4096
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct BlkDev {
pub name: String,
pub size: u64,
pub sector_size: u32,
pub size: BlkSize,
pub sector_size: BlkSize,
}
impl std::fmt::Display for BlkDev {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{} ({})", &self.name, format_bytes_size(self.size))
write!(f, "{} ({})", &self.name, self.size)
}
}
impl BlkDev {
pub fn new(name: String, size: u64) -> Self {
pub fn new(name: String, size: BlkSize) -> Self {
Self {
name: name,
size: size,
sector_size: 4096,
sector_size: BlkSize::new(4096),
}
}
pub fn from_hash_map(data: &HashMap<String, String>) -> Option<Self> {
Some(Self {
name: data.get("NAME")?.clone(),
size: u64::from_str_radix(data.get("SIZE")?, 10).ok()?,
sector_size: 4096,
size: BlkSize::from_str_bytes(data.get("SIZE")?)?,
sector_size: BlkSize::new(4096),
})
}
/// this is blocking function
@@ -151,15 +241,26 @@ impl FSType {
// }
// }
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum PartRole {
EFI,
ROOT,
HOME,
SWAP,
LuksRoot,
LuksHome,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct PartInfo {
pub name: String,
pub size: u64,
pub start: u64,
pub size: BlkSize,
pub start: BlkSize,
pub fs_type: FSType,
pub gpt_label: Option<String>,
pub fs_label: Option<String>,
pub mount_point: Option<String>,
pub role: Option<PartRole>,
}
impl std::fmt::Display for PartInfo {
@@ -169,7 +270,7 @@ impl std::fmt::Display for PartInfo {
"{} {} {}",
&self.name,
&self.fs_type,
format_bytes_size(self.size)
self.size
)
}
}
@@ -178,8 +279,8 @@ impl PartInfo {
pub fn from_hash_map(data: &HashMap<String, String>) -> Option<Self> {
Some(Self {
name: data.get("NAME")?.clone(),
size: u64::from_str_radix(data.get("SIZE")?, 10).ok()?,
start: 0,
size: BlkSize::from_str_bytes(data.get("SIZE")?)?,
start: BlkSize::new(0),
fs_type: FSType::from_str(data.get("FSTYPE")?),
gpt_label: data
.get("PARTLABEL")
@@ -190,6 +291,7 @@ impl PartInfo {
mount_point: data
.get("MOUNTPOINT")
.and_then(|v| if v.is_empty() { None } else { Some(v.clone()) }),
role: None,
})
}
}
@@ -287,7 +389,7 @@ pub fn align_part(size: u64, start: u64, align: u64) -> Option<(u64, u64, u64)>
pub struct PartLayout {
pub dev: BlkDev,
pub empty_space: u64,
pub empty_space: BlkSize,
pub part_list: Vec<PartInfo>,
}
@@ -302,7 +404,7 @@ impl PartLayout {
pub fn read_from_disk(dev: &BlkDev) -> Result<Self, String> {
let part_list = get_dev_part_blocking(&dev.name)?;
let parts_size = part_list.iter().fold(0, |acc, part| acc + part.size);
let parts_size = part_list.iter().fold(BlkSize::new(0), |acc, part| acc + part.size);
Ok(Self {
dev: dev.clone(),
empty_space: dev.size - parts_size,
@@ -318,45 +420,86 @@ impl PartLayout {
pub fn add_part(
mut self,
part_size: u64,
part_size: BlkSize,
fs_type: FSType,
gpt_label: Option<String>,
fs_label: Option<String>,
mount_point: Option<String>,
role: Option<PartRole>,
) -> Self {
let part_start = if self.part_list.is_empty() {
2u64 * 1024u64 * 1024u64
BlkSize::new_mb(2)
} else {
self.dev.size.checked_sub(self.empty_space).unwrap()
self.dev.size - self.empty_space
};
let (size, start, _) =
align_part(part_size, part_start, self.dev.sector_size as u64).unwrap();
align_part(part_size.b(), part_start.b(), self.dev.sector_size.b()).unwrap();
let part_name = format!("{}p{}", self.dev.name, self.part_list.len() + 1);
self.part_list.push(PartInfo {
name: "_".into(),
size: size,
start: start,
name: part_name,
size: BlkSize::new(size),
start: BlkSize::new(start),
fs_type: fs_type,
gpt_label: gpt_label,
fs_label: fs_label,
mount_point: mount_point,
role: role,
});
self.empty_space = self.empty_space.checked_sub(size).unwrap();
self.empty_space = self.empty_space - BlkSize::new(size);
self
}
pub fn add_part_reserve(
self,
reserve_space: u64,
reserve_space: BlkSize,
fs_type: FSType,
gpt_label: Option<String>,
fs_label: Option<String>,
mount_point: Option<String>,
role: Option<PartRole>,
) -> Self {
let p_size = self.empty_space.checked_sub(reserve_space).unwrap();
self.add_part(p_size, fs_type, gpt_label, fs_label, mount_point)
let p_size = self.empty_space - reserve_space;
self.add_part(p_size, fs_type, gpt_label, fs_label, mount_point, role)
}
pub fn gen_classic_layout(dev: BlkDev, efi_size: BlkSize, swap_size: BlkSize) -> Self {
let mut res: PartLayout = PartLayout::new(dev);
// UEFI partition
res = res
.add_part(
efi_size,
FSType::VFAT,
Some("efi".into()),
Some("EFI".into()),
Some("/efi".into()),
Some(PartRole::EFI),
)
.add_part_reserve(
swap_size,
FSType::XFS,
Some("root".into()),
None,
Some("/".into()),
Some(PartRole::ROOT),
);
// swap
if swap_size.b() > 0 {
res = res.add_part(
swap_size,
FSType::SWAP,
Some("swap".into()),
None,
Some("SWAP".into()),
Some(PartRole::SWAP),
);
}
res
}
}
+97 -50
View File
@@ -21,10 +21,10 @@
use crate::stage::{StageAction, StageResult};
use crate::kira_color_bar;
use crate::kira_disk_layout::{BlkDev,PartInfo, PartLayout, FSType};
use toml::Table;
use iced::{Alignment, Color, Length, widget};
use crate::kira_disk_layout::{BlkDev, BlkSize, FSType, PartInfo, PartLayout};
use iced::{Alignment, Color, widget};
use rust_i18n::t;
use toml::Table;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SwapMode {
@@ -61,13 +61,12 @@ fn part_type_to_color(t: &FSType) -> Color {
}
}
fn part_to_ct_params(part: &PartInfo, dev_size: u64) -> (String, u16, Color) {
let fill_ratio: u16 = ((part.size as f32 / dev_size as f32) * 12.0) as u16;
let fill_ratio = fill_ratio.max(1);
let part_color = part_type_to_color(&part.fs_type);
(part.to_string(), fill_ratio, part_color)
}
fn part_to_ct_params(part: &PartInfo, dev_size: BlkSize) -> (String, u16, Color) {
let fill_ratio: u16 = ((part.size.b() as f32 / dev_size.b() as f32) * 11.0) as u16;
let fill_ratio = fill_ratio.max(1);
let part_color = part_type_to_color(&part.fs_type);
(part.to_string(), fill_ratio, part_color)
}
#[derive(Debug, Clone)]
pub struct PartitionStage {
@@ -77,6 +76,7 @@ pub struct PartitionStage {
use_zram: bool,
secure_boot: bool,
selected_disk_parts: Option<Vec<(String, u16, Color)>>,
generated_disk_parts: Option<Vec<(String, u16, Color)>>,
}
#[derive(Debug, Clone)]
@@ -89,32 +89,55 @@ pub enum Message {
Back,
}
impl PartitionStage {
pub fn gen_disk_layout_estimate(&self) -> PartLayout {
let dev = self.device.clone().unwrap();
// pub fn gen_disk_layout_estimate(&self) -> PartLayout {
// let dev = self.device.clone().unwrap();
let swap_size: u64 = match self.swap_mode {
Some(SwapMode::NoSwap) => 0,
Some(SwapMode::SwapHibernate) => 666*1024*1024,
Some(SwapMode::SwapNoHibernate) => 1024*1024*1024,
_ => 1024*1024*1024,
};
let mut res: PartLayout = PartLayout::new(dev);
// let swap_size: u64 = match self.swap_mode {
// Some(SwapMode::NoSwap) => 0,
// Some(SwapMode::SwapHibernate) => 666 * 1024 * 1024,
// Some(SwapMode::SwapNoHibernate) => 1024 * 1024 * 1024,
// _ => 1024 * 1024 * 1024,
// };
// let mut res: PartLayout = PartLayout::new(dev);
// UEFI partition
res = res.add_part(256*1024*1024, FSType::VFAT, Some("kira-efi".into()), Some("EFI".into()), Some("/efi".into()))
.add_part(512*1024*1024, FSType::XFS, Some("kira-boot-a".into()), None, Some("/".into()))
.add_part_reserve(swap_size, FSType::XFS, Some("kira-home".into()), None, Some("/home".into()));
// swap
if swap_size > 0 {
res = res.add_part(swap_size, FSType::SWAP, Some("kira-swap".into()), None, Some("SWAP".into()));
}
// // UEFI partition
// res = res
// .add_part(
// 256 * 1024 * 1024,
// FSType::VFAT,
// Some("kira-efi".into()),
// Some("EFI".into()),
// Some("/efi".into()),
// )
// .add_part(
// 512 * 1024 * 1024,
// FSType::XFS,
// Some("kira-boot-a".into()),
// None,
// Some("/".into()),
// )
// .add_part_reserve(
// swap_size,
// FSType::XFS,
// Some("kira-home".into()),
// None,
// Some("/home".into()),
// );
res
}
// // swap
// if swap_size > 0 {
// res = res.add_part(
// swap_size,
// FSType::SWAP,
// Some("kira-swap".into()),
// None,
// Some("SWAP".into()),
// );
// }
// res
// }
pub fn new(toml_config: &Table) -> Result<Self, String> {
let maybe_dev_list = BlkDev::list_sys_blk_dev();
@@ -126,13 +149,13 @@ impl PartitionStage {
.and_then(|v| v.as_integer())
.unwrap_or(8192);
let min_disk_size: u64 = min_disk_size_mb.try_into().unwrap_or(8192) * 1024 * 1024;
let min_disk_size: BlkSize = BlkSize::new_mb(min_disk_size_mb.try_into().unwrap_or(8192));
match maybe_dev_list {
Ok(dev_list) => {
let acepted_dev: Vec<BlkDev> = dev_list
.iter()
.filter(|v| v.size >= min_disk_size)
.filter(|v| v.size.b() >= min_disk_size.b())
.cloned()
.collect();
@@ -143,6 +166,7 @@ impl PartitionStage {
use_zram: true,
secure_boot: false,
selected_disk_parts: None,
generated_disk_parts: None,
})
}
Err(ex) => Err(ex),
@@ -174,7 +198,8 @@ impl PartitionStage {
Message::SelectDevice(dev) => {
self.selected_disk_parts = match PartLayout::read_from_disk(&dev) {
Ok(dev_parts) => Some(
dev_parts.part_list
dev_parts
.part_list
.iter()
.map(|part_inf| part_to_ct_params(&part_inf, dev.size))
.collect(),
@@ -185,10 +210,44 @@ impl PartitionStage {
}
};
let swap_size: BlkSize = match self.swap_mode {
Some(SwapMode::NoSwap) => BlkSize::new(0),
Some(SwapMode::SwapHibernate) => BlkSize::new_gb(4),
Some(SwapMode::SwapNoHibernate) => BlkSize::new_gb(2),
_ => BlkSize::new_gb(2),
};
self.generated_disk_parts = Some(
PartLayout::gen_classic_layout(dev.clone(), BlkSize::new_mb(512), swap_size)
.part_list
.iter()
.map(|part_info| part_to_ct_params(part_info, dev.size))
.collect(),
);
self.device = Some(dev);
StageAction::None
}
Message::SelectSwapMode(sm) => {
if let Some(dev) = self.device.clone() {
let swap_size: BlkSize = match sm {
SwapMode::NoSwap => BlkSize::new(0),
SwapMode::SwapHibernate => BlkSize::new_gb(4),
SwapMode::SwapNoHibernate => BlkSize::new_gb(2),
};
self.generated_disk_parts = Some(
PartLayout::gen_classic_layout(
dev.clone(),
BlkSize::new_mb(512),
swap_size,
)
.part_list
.iter()
.map(|part_info| part_to_ct_params(part_info, dev.size))
.collect(),
);
}
self.swap_mode = Some(sm);
StageAction::None
}
@@ -206,23 +265,11 @@ impl PartitionStage {
}
pub fn view(&self) -> iced::Element<'_, Message> {
let dev_parts_es: Option<Vec<(String, u16, Color)>> = if self.selected_disk_parts.is_some()
{
let p_l = self.gen_disk_layout_estimate();
Some(
p_l.part_list.iter()
.map(|part_info| part_to_ct_params(part_info, p_l.dev.size))
.collect()
)
} else {
None
};
let dev_parts_text = match dev_parts_es {
let dev_parts_text = match self.generated_disk_parts {
Some(_) => widget::text(t!("partition.current_after_install")),
None => widget::text(""),
};
let dev_parts_content = kira_color_bar::color_bar(dev_parts_es, 48);
let dev_parts_content = kira_color_bar::color_bar(self.generated_disk_parts.clone(), 56);
let dev_parts_content = widget::column![dev_parts_text, dev_parts_content];
let back_button = widget::button(widget::text(t!("button.back"))).on_press(Message::Back);
@@ -232,7 +279,7 @@ impl PartitionStage {
Some(_) => widget::text(t!("partition.current_dev_layout")),
None => widget::text(""),
};
let selected_dev_content = kira_color_bar::color_bar(self.selected_disk_parts.clone(), 48);
let selected_dev_content = kira_color_bar::color_bar(self.selected_disk_parts.clone(), 56);
let selected_dev_content = widget::column![selected_dev_text, selected_dev_content];
widget::column![