From 2684c12e7373aba4a5003dd52686eebe24e4d42df4eff6c8fbd7206650c2e5fc Mon Sep 17 00:00:00 2001 From: Kira Date: Sat, 9 May 2026 21:50:46 +0200 Subject: [PATCH] Initial work on partition stage. --- src/locales/en.json | 7 +- src/main.rs | 24 +++- src/stages/mod.rs | 3 +- src/stages/partition/mod.rs | 215 ++++++++++++++++++++++++++++++++++++ 4 files changed, 246 insertions(+), 3 deletions(-) create mode 100644 src/stages/partition/mod.rs diff --git a/src/locales/en.json b/src/locales/en.json index 9d98825..19c2322 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -26,5 +26,10 @@ "keyboard.select_model": "Select keyboard model", "keyboard.select_layout": "Select keyboard layout", "keyboard.select_variant": "Select keyboard variant", - "keyboard.init_error": "Error getting keyboard layouts information from the system!" + "keyboard.init_error": "Error getting keyboard layouts information from the system!", + "partition.no_swap": "Do not create swap partition", + "partition.swap_no_hibernate": "Swap without hibernate", + "partition.swap_hibernate": "Swap partition with hibernation support", + "partition.select_device_placeholder": "Select Device", + "partition.select_device": "Select device for system installation" } \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index b1310e3..f7b04a0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -45,6 +45,7 @@ enum Views { TimeZone(stages::timezone::TimeZoneStage), Locale(stages::locale::LocaleStage), Keyboard(stages::keyboard::KeyboardStage), + Partition(stages::partition::PartitionStage), } enum Message { @@ -55,6 +56,7 @@ enum Message { TimeZone(stages::timezone::Message), Locale(stages::locale::Message), Keyboard(stages::keyboard::Message), + Partition(stages::partition::Message), } struct KiraState { @@ -103,6 +105,7 @@ fn view(k_state: &KiraState) -> Element<'_, Message> { Views::TimeZone(timezone_stage) => timezone_stage.view().map(Message::TimeZone), Views::Locale(locale_stage) => locale_stage.view().map(Message::Locale), Views::Keyboard(keyboard_stage) => keyboard_stage.view().map(Message::Keyboard), + Views::Partition(partition_stage) => partition_stage.view().map(Message::Partition), } } @@ -228,7 +231,8 @@ fn update(k_state: &mut KiraState, message: Message) -> Task { match keyboard_view.update(keyboard_msg) { StageAction::Next(keyboard_res) => { k_state.config.config_trail.push(keyboard_res); - iced::exit() + k_state.current_view = Views::Partition(stages::partition::PartitionStage::new()); + Task::none() } StageAction::Back => { k_state.config.config_trail.pop(); @@ -240,6 +244,24 @@ fn update(k_state: &mut KiraState, message: Message) -> Task { } else { Task::none() } + }, + Message::Partition(partition_msg) => { + if let Views::Partition(partition_view) = &mut k_state.current_view { + match partition_view.update(partition_msg) { + StageAction::Next(partition_res) => { + k_state.config.config_trail.push(partition_res); + iced::exit() + } + StageAction::Back => { + k_state.config.config_trail.pop(); + k_state.current_view = Views::Keyboard(stages::keyboard::KeyboardStage::new()); + Task::none() + } + _ => Task::none(), + } + } else { + Task::none() + } } } } diff --git a/src/stages/mod.rs b/src/stages/mod.rs index 55242e0..6d80d26 100644 --- a/src/stages/mod.rs +++ b/src/stages/mod.rs @@ -20,4 +20,5 @@ pub mod license; pub mod network; pub mod timezone; pub mod locale; -pub mod keyboard; \ No newline at end of file +pub mod keyboard; +pub mod partition; diff --git a/src/stages/partition/mod.rs b/src/stages/partition/mod.rs new file mode 100644 index 0000000..cb13dbc --- /dev/null +++ b/src/stages/partition/mod.rs @@ -0,0 +1,215 @@ +// +// Copyright (C) <2026> + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +/* + This is partition stage! +*/ + +use crate::{ + kira_theming, + stage::{ConfigValue, StageAction, StageResult}, +}; +use iced::{ + Alignment, + widget::{self, text::Format}, +}; +use rust_i18n::t; +use std::collections::HashMap; + +#[derive(Debug, Clone)] +pub enum SwapMode { + NoSwap, + SwapNoHibernate, + SwapHibernate, +} + +impl std::fmt::Display for SwapMode { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::NoSwap => f.write_str(t!("partition.no_swap").as_ref()), + Self::SwapNoHibernate => f.write_str(t!("partition.swap_no_hibernate").as_ref()), + Self::SwapHibernate => f.write_str(t!("partition.swap_hibernate").as_ref()), + } + } +} + +#[derive(Debug, Clone)] +pub struct PartitionStage { + device: Option, + devices: Vec, + swap_mode: Option, + use_zram: bool, + secure_boot: bool, +} + +#[derive(Debug, Clone)] +pub enum Message { + SelectDevice(String), + SelectSwapMode(SwapMode), + ToggleZram(bool), + ToggleSecureBoot(bool), + Next, + Back, +} + +pub fn get_disks_info_blocking() -> Result, String> { + use std::process::Command; + + match Command::new("lsblk").args(["-n", "-d"]).output() { + Ok(cmd_output) => { + if cmd_output.status.success() { + match String::from_utf8(cmd_output.stdout) { + Ok(raw_str_list) => Ok(raw_str_list + .lines() + .map(|l| { + l.split_whitespace() + .map(str::to_string) + .collect::>() + }) + .filter(|l_v| l_v.len() == 6) + .map(|dev_params| dev_params[0].clone()) + .collect()), + Err(ex) => Err(format!( + "Exception while converting disks info to UTF8 {}", + ex + )), + } + } else { + Err(format!( + "Error getting disk devices list: {}", + String::from_utf8(cmd_output.stderr).unwrap_or("UNKNOWN".into()) + )) + } + } + Err(ex) => Err(ex.to_string()), + } +} + +impl PartitionStage { + pub fn new() -> Self { + let maybe_dev_list = get_disks_info_blocking(); + match maybe_dev_list { + Ok(dev_list) => Self { + device: None, + devices: dev_list, + swap_mode: Some(SwapMode::SwapNoHibernate), + use_zram: true, + secure_boot: false, + }, + Err(ex) => { + println!("{}", ex); + Self { + device: None, + devices: Vec::new(), + swap_mode: Some(SwapMode::SwapNoHibernate), + use_zram: true, + secure_boot: false, + } + } + } + } + + fn gen_result(&self) -> StageResult { + StageResult { + name: "partition".to_string(), + config: Some(HashMap::from([ + ( + "device".to_string(), + ConfigValue::String(self.device.clone().unwrap_or_else(|| "NONE".to_string())), + ), + ( + "swap_mode".to_string(), + ConfigValue::String( + self.swap_mode + .clone() + .unwrap_or(SwapMode::NoSwap) + .to_string(), + ), + ), + ("zram".to_string(), ConfigValue::Bool(self.use_zram)), + ( + "secure_boot".to_string(), + ConfigValue::Bool(self.secure_boot), + ), + ])), + resuts: None, + error: None, + } + } + + pub fn update(&mut self, message: Message) -> StageAction { + match message { + Message::SelectDevice(dev) => { + self.device = Some(dev); + StageAction::None + } + Message::SelectSwapMode(sm) => { + self.swap_mode = Some(sm); + StageAction::None + } + Message::ToggleSecureBoot(t) => { + self.secure_boot = t; + StageAction::None + } + Message::ToggleZram(t) => { + self.use_zram = t; + StageAction::None + } + Message::Back => StageAction::Back, + Message::Next => StageAction::Next(self.gen_result()), + } + } + + pub fn view(&self) -> iced::Element<'_, Message> { + let back_button = widget::button(widget::text(t!("button.back"))).on_press(Message::Back); + let next_button = widget::button(widget::text(t!("button.next"))).on_press(Message::Next); + + // Embed the image bytes into the executable + let welcom_logo_handle = widget::image::Handle::from_bytes(kira_theming::get_logo_bytes()); + + widget::column![ + widget::container( + widget::column![ + widget::text(t!("partition.select_device")), + widget::pick_list( + self.devices.clone(), + self.device.clone(), + Message::SelectDevice, + ) + .placeholder(t!("partition.select_device_placeholder")) + ] + .padding(10) + .spacing(10) + .align_x(Alignment::Center) + ) + .height(iced::Length::Fill) + .width(iced::Length::Fill) + .align_x(Alignment::Center) + .align_y(Alignment::Center), + widget::row![ + back_button, + widget::space::horizontal(), // Pushes the right button to the far right + next_button + ] + .width(iced::Length::Fill) + .align_y(Alignment::End) + .padding(10), + ] + .align_x(Alignment::Center) + .spacing(10) + .into() + } +}