Compare commits

...
9 Commits
18 changed files with 1130 additions and 256 deletions
Generated
+30 -30
View File
@@ -512,9 +512,9 @@ dependencies = [
[[package]] [[package]]
name = "cc" name = "cc"
version = "1.2.62" version = "1.2.63"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1dce859f0832a7d088c4f1119888ab94ef4b5d6795d1ce05afb7fe159d79f98" checksum = "556e016178bb5662a08681bbe0f00f8e17631781a4dfc8c45e466e4b185ec27f"
dependencies = [ dependencies = [
"find-msvc-tools", "find-msvc-tools",
"jobserver", "jobserver",
@@ -1872,14 +1872,14 @@ checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981"
[[package]] [[package]]
name = "libredox" name = "libredox"
version = "0.1.16" version = "0.1.17"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e02f3bb43d335493c96bf3fd3a321600bf6bd07ed34bc64118e9293bdffea46c" checksum = "f02ab6bace2054fb888a3c16f990117b579d14a3088e472d63c6011fa185c9d3"
dependencies = [ dependencies = [
"bitflags 2.11.1", "bitflags 2.11.1",
"libc", "libc",
"plain", "plain",
"redox_syscall 0.7.5", "redox_syscall 0.8.0",
] ]
[[package]] [[package]]
@@ -1957,9 +1957,9 @@ dependencies = [
[[package]] [[package]]
name = "memchr" name = "memchr"
version = "2.8.0" version = "2.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" checksum = "6b947ae49db0d222b1dbc6b113ce7248a3fc3a6ca21b696717bfc000ba4484d8"
[[package]] [[package]]
name = "memmap2" name = "memmap2"
@@ -2755,7 +2755,7 @@ version = "3.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e67ba7e9b2b56446f1d419b1d807906278ffa1a658a8a5d8a39dcb1f5a78614f" checksum = "e67ba7e9b2b56446f1d419b1d807906278ffa1a658a8a5d8a39dcb1f5a78614f"
dependencies = [ dependencies = [
"toml_edit 0.25.11+spec-1.1.0", "toml_edit 0.25.12+spec-1.1.0",
] ]
[[package]] [[package]]
@@ -2997,9 +2997,9 @@ dependencies = [
[[package]] [[package]]
name = "redox_syscall" name = "redox_syscall"
version = "0.7.5" version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4666a1a60d8412eab19d94f6d13dcc9cea0a5ef4fdf6a5db306537413c661b1b" checksum = "7c7591fa2c6b601dfcfe5f043f65a1c39fcdf50efefcd7f1572e538c1f4b398d"
dependencies = [ dependencies = [
"bitflags 2.11.1", "bitflags 2.11.1",
] ]
@@ -3278,9 +3278,9 @@ dependencies = [
[[package]] [[package]]
name = "shlex" name = "shlex"
version = "1.3.0" version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba"
[[package]] [[package]]
name = "signal-hook-registry" name = "signal-hook-registry"
@@ -3676,9 +3676,9 @@ dependencies = [
[[package]] [[package]]
name = "toml_edit" name = "toml_edit"
version = "0.25.11+spec-1.1.0" version = "0.25.12+spec-1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b59c4d22ed448339746c59b905d24568fcbb3ab65a500494f7b8c3e97739f2b" checksum = "d2153edc6955a6c354fad8f5efd38b6a8769bdccf9fe50f8e1329f81b0baa5d7"
dependencies = [ dependencies = [
"indexmap", "indexmap",
"toml_datetime 1.1.1+spec-1.1.0", "toml_datetime 1.1.1+spec-1.1.0",
@@ -3819,9 +3819,9 @@ checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861"
[[package]] [[package]]
name = "uuid" name = "uuid"
version = "1.23.1" version = "1.23.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ddd74a9687298c6858e9b88ec8935ec45d22e8fd5e6394fa1bd4e99a87789c76" checksum = "d258b83ceec21034727ecee8c382cfa6c3e133699b0742c64571814fb420c9f7"
dependencies = [ dependencies = [
"js-sys", "js-sys",
"serde_core", "serde_core",
@@ -4548,9 +4548,9 @@ checksum = "e01738255b5a16e78bbb83e7fbba0a1e7dd506905cfc53f4622d89015a03fbb5"
[[package]] [[package]]
name = "zbus" name = "zbus"
version = "5.15.0" version = "5.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3bcbf15c8708d7fc1be0c993622e0a5cbd5e8b52bfa40afa4c3e0cd8d724ac1" checksum = "eee682d202a77e4a9f3b2c2bdf48a7b28af5c08c34ddf66f98c93e5e39464285"
dependencies = [ dependencies = [
"async-broadcast", "async-broadcast",
"async-executor", "async-executor",
@@ -4583,9 +4583,9 @@ dependencies = [
[[package]] [[package]]
name = "zbus_macros" name = "zbus_macros"
version = "5.15.0" version = "5.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "51fa5406ad9175a8c825a931f8cf347116b531b3634fcb0b627c290f1f2516ff" checksum = "adf1bd45a81a103745b1757754762a26e8cd01e4532e4d6c8ec431624b80d1d6"
dependencies = [ dependencies = [
"proc-macro-crate", "proc-macro-crate",
"proc-macro2", "proc-macro2",
@@ -4615,18 +4615,18 @@ checksum = "6df3dc4292935e51816d896edcd52aa30bc297907c26167fec31e2b0c6a32524"
[[package]] [[package]]
name = "zerocopy" name = "zerocopy"
version = "0.8.48" version = "0.8.50"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eed437bf9d6692032087e337407a86f04cd8d6a16a37199ed57949d415bd68e9" checksum = "3b065d4f0e55f82fae73202e189638116a87c55ab6b8e6c2721e13dd9d854ad1"
dependencies = [ dependencies = [
"zerocopy-derive", "zerocopy-derive",
] ]
[[package]] [[package]]
name = "zerocopy-derive" name = "zerocopy-derive"
version = "0.8.48" version = "0.8.50"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70e3cd084b1788766f53af483dd21f93881ff30d7320490ec3ef7526d203bad4" checksum = "0b631b19d36a892ab55420c92dbc83ccd79274f25be714855d3074aa71cab639"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@@ -4665,9 +4665,9 @@ dependencies = [
[[package]] [[package]]
name = "zvariant" name = "zvariant"
version = "5.11.0" version = "5.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1c1567a6ec68df868cbbfde844cfc6d81649fe5109a62b116b19fabd53e618ee" checksum = "a192a0bde63360d77a7523c833d4b4ce6070a927e2c53246e4c540b1a3e27be0"
dependencies = [ dependencies = [
"endi", "endi",
"enumflags2", "enumflags2",
@@ -4679,9 +4679,9 @@ dependencies = [
[[package]] [[package]]
name = "zvariant_derive" name = "zvariant_derive"
version = "5.11.0" version = "5.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c7d5b780599bbde114e39d9a0799577fad1ced5105d38515745f7b3099d8ceda" checksum = "90bc6cde9c01c511074be97f7ccb6c19d0da89e3f8662e812e999dcfd4638737"
dependencies = [ dependencies = [
"proc-macro-crate", "proc-macro-crate",
"proc-macro2", "proc-macro2",
@@ -4692,9 +4692,9 @@ dependencies = [
[[package]] [[package]]
name = "zvariant_utils" name = "zvariant_utils"
version = "3.3.1" version = "3.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d464f5733ffa07a3164d656f18533caace9d0638596721355d73256a410d691" checksum = "1e8535915cfa75547e559d8c68e8139909a4aeee076831e4ef7fc59d8172c4d6"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
+1
View File
@@ -4,3 +4,4 @@ reqire_network = true
[partition] [partition]
min_disk_size_mb = 8192 min_disk_size_mb = 8192
efi_size_mb = 128
+233 -16
View File
@@ -18,9 +18,9 @@
Functions for working with disk layout Functions for working with disk layout
*/ */
use crate::kira_size::KiraSize;
use log; use log;
use std::collections::HashMap; use std::{collections::HashMap};
use crate::kira_size::{KiraSize};
/// ///
/// size - device size in bytes /// size - device size in bytes
/// sector size - default 4096 /// sector size - default 4096
@@ -38,6 +38,7 @@ impl std::fmt::Display for BlkDev {
} }
impl BlkDev { impl BlkDev {
/// Create new BlkDev struct with specifyed name and size
pub fn new(name: String, size: KiraSize) -> Self { pub fn new(name: String, size: KiraSize) -> Self {
Self { Self {
name: name, name: name,
@@ -45,6 +46,9 @@ impl BlkDev {
sector_size: KiraSize::new_b(4096), sector_size: KiraSize::new_b(4096),
} }
} }
/// Create new BlkDev struct with name and size parsed from hashmap
/// that contains String key val pairs with "NAME" and "SIZE" keys
pub fn from_hash_map(data: &HashMap<String, String>) -> Option<Self> { pub fn from_hash_map(data: &HashMap<String, String>) -> Option<Self> {
Some(Self { Some(Self {
name: data.get("NAME")?.clone(), name: data.get("NAME")?.clone(),
@@ -52,7 +56,13 @@ impl BlkDev {
sector_size: KiraSize::new_b(4096), sector_size: KiraSize::new_b(4096),
}) })
} }
/// returns dev name in a "/dev/name" form
pub fn full_name(&self) -> String {
format!("/dev/{}", self.name)
}
/// this is blocking function /// this is blocking function
/// Lists block devices in system thru "lsbk" command
pub fn list_sys_blk_dev() -> Result<Vec<Self>, String> { pub fn list_sys_blk_dev() -> Result<Vec<Self>, String> {
use std::process::Command; use std::process::Command;
@@ -96,6 +106,11 @@ impl BlkDev {
Err(ex) => Err(ex.to_string()), Err(ex) => Err(ex.to_string()),
} }
} }
pub fn from_sys(name: &str) -> Option<Self> {
Self::list_sys_blk_dev().ok()?.iter().find(|v| v.name == name).cloned()
}
} }
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]
@@ -139,6 +154,22 @@ impl FSType {
_ => Self::UNKNOWN, _ => Self::UNKNOWN,
} }
} }
/// return fs type that can be used with parted utility
pub fn to_parted_str(&self) -> &str {
match self {
Self::VFAT => "fat32",
Self::XFS => "xfs",
Self::LUKS => "luks",
Self::SWAP => "linux-swap",
Self::EXT4 => "ext4",
Self::BTRFS => "btrfs",
// workaroud as parted do not support bcachefs partition type
Self::BCACHEFS => "bcachefs",
// lets do something generic here instead of panic
Self::UNKNOWN => "linux-swap",
}
}
} }
// fn part_type_to_color(t: &str) -> Color { // fn part_type_to_color(t: &str) -> Color {
@@ -177,13 +208,7 @@ pub struct PartInfo {
impl std::fmt::Display for PartInfo { impl std::fmt::Display for PartInfo {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!( write!(f, "{} {} {}", &self.name, &self.fs_type, self.size)
f,
"{} {} {}",
&self.name,
&self.fs_type,
self.size
)
} }
} }
@@ -291,7 +316,8 @@ pub fn align_part(size: u64, start: u64, align: u64) -> Option<(u64, u64, u64)>
if align_size < align { if align_size < align {
log::warn!( log::warn!(
"!!! align_size < align !!! align_size: {} align: {}", "!!! align_size < align !!! align_size: {} align: {}",
align_size, align align_size,
align
); );
return None; return None;
} }
@@ -299,6 +325,7 @@ pub fn align_part(size: u64, start: u64, align: u64) -> Option<(u64, u64, u64)>
Some((align_size, align_start, align_end)) Some((align_size, align_start, align_end))
} }
#[derive(Debug, Clone, PartialEq)]
pub struct PartLayout { pub struct PartLayout {
pub dev: BlkDev, pub dev: BlkDev,
pub empty_space: KiraSize, pub empty_space: KiraSize,
@@ -316,7 +343,9 @@ impl PartLayout {
pub fn read_from_disk(dev: &BlkDev) -> Result<Self, String> { pub fn read_from_disk(dev: &BlkDev) -> Result<Self, String> {
let part_list = get_dev_part_blocking(&dev.name)?; let part_list = get_dev_part_blocking(&dev.name)?;
let parts_size = part_list.iter().fold(KiraSize::new_b(0), |acc, part| acc + part.size); let parts_size = part_list
.iter()
.fold(KiraSize::new_b(0), |acc, part| acc + part.size);
Ok(Self { Ok(Self {
dev: dev.clone(), dev: dev.clone(),
empty_space: dev.size - parts_size, empty_space: dev.size - parts_size,
@@ -377,7 +406,7 @@ impl PartLayout {
self.add_part(p_size, fs_type, gpt_label, fs_label, mount_point, role) self.add_part(p_size, fs_type, gpt_label, fs_label, mount_point, role)
} }
pub fn gen_classic_layout(dev: BlkDev, efi_size: KiraSize, swap_size: KiraSize) -> Self { pub fn gen_kira_layout(dev: BlkDev, efi_size: KiraSize, swap_size: KiraSize) -> Self {
let mut res: PartLayout = PartLayout::new(dev); let mut res: PartLayout = PartLayout::new(dev);
// UEFI partition // UEFI partition
@@ -385,15 +414,15 @@ impl PartLayout {
.add_part( .add_part(
efi_size, efi_size,
FSType::VFAT, FSType::VFAT,
Some("efi".into()), Some("bee-efi".into()),
Some("EFI".into()), Some("EFI".into()),
Some("/efi".into()), Some("/efi".into()),
Some(PartRole::EFI), Some(PartRole::EFI),
) )
.add_part_reserve( .add_part_reserve(
swap_size, swap_size,
FSType::XFS, FSType::BTRFS,
Some("root".into()), Some("bee-root".into()),
None, None,
Some("/".into()), Some("/".into()),
Some(PartRole::ROOT), Some(PartRole::ROOT),
@@ -415,6 +444,195 @@ impl PartLayout {
} }
} }
/// Executes parted command with given arguments
fn exec_parted(args: &Vec<&str>) -> Result<String, String> {
use std::process::Command;
match Command::new("parted").args(args).output() {
Err(ex) => Err(ex.to_string()),
Ok(out) => {
if out.status.success() {
String::from_utf8(out.stdout).map_err(|ex| ex.to_string())
} else {
Err(out.status.to_string())
}
}
}
}
fn parted_get_last_part_num(dev: &BlkDev) -> Result<u32, String> {
// parted -m /dev/loop0 print
match exec_parted(&vec!["-m", dev.full_name().as_str(), "print"]) {
Ok(res) => {
match res.lines().last().and_then(|last_line| {
last_line
.split_once(':')
.and_then(|(first_part, _)| u32::from_str_radix(first_part, 10).ok())
}) {
Some(n) => Ok(n),
None => Err("Error parsing parted output".to_string()),
}
}
Err(ex) => Err(ex),
}
}
/// init gpt partition table
/// parted --script $install_device mklabel gpt
/// Its blocking function
pub fn parted_init_gpt_part_table(dev: &BlkDev) -> Result<(), String> {
exec_parted(&vec![
"--script",
dev.full_name().as_str(),
"mklabel",
"gpt",
])
.map(|_| ())
}
const LINUX_ROOT_X86_64_TYPE: &str = "4F68BCE3-E8CD-4DB1-96E7-FBCAF984B709";
/// creating partition from PartInfo specification
/// It assumes that we add partitions sequentially to empty space (for part attributes settings)
/// Otherwise can produce bonkers layout
pub fn parted_mkpart(dev: &BlkDev, part: &PartInfo) -> Result<(), String> {
let part_end = part.start + part.size;
// gpt label should be in double qutes
let gpt_label = if let Some(s) = &part.gpt_label {
format!("\"{}\"", s)
} else {
"\"\"".to_string()
};
// creating partition
exec_parted(&vec![
"--script",
dev.full_name().as_str(),
"mkpart",
gpt_label.as_str(),
part.fs_type.to_parted_str(),
part.start.to_parted_bytes_str().as_str(),
part_end.to_parted_bytes_str().as_str(),
])?;
// set partition options depending on its role
if let Some(role) = &part.role {
// getting part number of last created partition (hopefully)
let part_id = parted_get_last_part_num(dev)?;
match role {
PartRole::EFI => {
exec_parted(&vec![
"--script",
dev.full_name().as_str(),
"set",
&part_id.to_string(),
"esp",
"on",
])?;
}
// parted --script $install_device type 3 4F68BCE3-E8CD-4DB1-96E7-FBCAF984B709
PartRole::ROOT => {
exec_parted(&vec![
"--script",
dev.full_name().as_str(),
"type",
&part_id.to_string(),
LINUX_ROOT_X86_64_TYPE,
])?;
}
_ => (),
}
};
Ok(())
}
/// creating single drive bcachefs volume
pub fn bcachefs_format(
dev: &BlkDev,
compression: Option<String>,
password: Option<String>,
) -> Result<(), String> {
// sudo bcachefs format --compression=lz4 --background_compression=lz4 --encrypted /dev/loop0
// --data_checksum=none - do not make sense on single dev volumes
// sudo bcachefs format --force --compression=lz4 --background_compression=lz4 --encrypted --passphrase_file=./test.pass /dev/loop0
use std::fs;
use std::process::Command;
let mut arg = vec!["format", "--force", "--data_checksum=none"];
// if compression algorythm specified
if let Some(compresss_alg) = &compression {
arg.push(compresss_alg.as_str());
}
// if we want encrypted volume
if let Some(passw) = password {
// creating tmp file with password
fs::write("/tmp/bzzpsspass.txt", &passw).map_err(|ex| ex.to_string())?;
arg.push("--encrypted");
arg.push("--passphrase_file=/tmp/bzzpsspass.txt");
}
// add dev name at the end
let dev_name = dev.full_name();
arg.push(dev_name.as_str());
let out = Command::new("bcachefs")
.args(arg)
.output()
.map_err(|ex| ex.to_string())?;
// ignore any errors while deleting pass tmp file
let _ = fs::remove_file("/tmp/bzzpsspass.txt");
if out.status.success() {
Ok(())
} else {
Err(String::from_utf8(out.stderr).map_err(|ex| ex.to_string())?)
}
}
/// unlok encrypted bcache volume
pub fn bcachefs_unlock(dev: &BlkDev, pass: String) -> Result<(), String> {
use std::fs;
use std::process::Command;
// bcachefs unlock --file=/tmp/bzzpsspass.txt /dev/loop0
let dev_name = dev.full_name();
let arg = vec!["unlock", "--file=/tmp/bzzpsspass.txt ", dev_name.as_str()];
// creating tmp file with password
fs::write("/tmp/bzzpsspass.txt", &pass).map_err(|ex| ex.to_string())?;
let out = Command::new("bcachefs")
.args(arg)
.output()
.map_err(|ex| ex.to_string())?;
// ignore any errors while deleting pass tmp file
let _ = fs::remove_file("/tmp/bzzpsspass.txt");
if out.status.success() {
Ok(())
} else {
Err(String::from_utf8(out.stderr).map_err(|ex| ex.to_string())?)
}
}
/// create subvolume
pub fn bcachefs_create_subvolume(vol_path: &str) -> Result<(), String> {
use std::process::Command;
// sudo bcachefs subvolume create /mnt/test/nyan
let out = Command::new("bcachefs")
.args(["subvolume", "create", vol_path])
.output()
.map_err(|ex| ex.to_string())?;
if out.status.success() {
Ok(())
} else {
Err(String::from_utf8(out.stderr).map_err(|ex| ex.to_string())?)
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use rand::RngExt; use rand::RngExt;
@@ -466,4 +684,3 @@ mod tests {
} }
} }
} }
+7
View File
@@ -46,6 +46,13 @@ impl std::fmt::Display for KiraSize {
impl KiraSize { impl KiraSize {
pub const SUFFIXS: [&str; 5] = ["KB", "MB", "GB", "TB", "PB"]; pub const SUFFIXS: [&str; 5] = ["KB", "MB", "GB", "TB", "PB"];
/// returns size in bytes suffixed with "B" character
pub fn to_parted_bytes_str(&self)-> String {
format!("{}B", self.size_bytes)
}
pub fn new_b(size:u64) -> Self { pub fn new_b(size:u64) -> Self {
Self { size_bytes: size } Self { size_bytes: size }
} }
+1 -1
View File
@@ -96,7 +96,7 @@ pub fn get_gpus_list() -> Vec<GpuInfo> {
res res
} }
use serde::{Deserialize, Serialize}; use serde::Deserialize;
#[derive(Debug, Clone, Deserialize)] #[derive(Debug, Clone, Deserialize)]
pub struct SecureBootStatus { pub struct SecureBootStatus {
+15 -2
View File
@@ -4,7 +4,10 @@
"button.next": "Next", "button.next": "Next",
"button.back": "Back", "button.back": "Back",
"button.cancel": "Cancel", "button.cancel": "Cancel",
"button.finish": "Finish",
"button.exit": "Exit", "button.exit": "Exit",
"button.yes": "Yes",
"button.no": "No",
"wellcome.text": "Welcome to Kira Installer!", "wellcome.text": "Welcome to Kira Installer!",
"wellcome.choose_language": "Please select language to use during istalation!", "wellcome.choose_language": "Please select language to use during istalation!",
"license.license": "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.\n\nThis 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.\n\nYou should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.", "license.license": "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.\n\nThis 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.\n\nYou should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.",
@@ -37,7 +40,17 @@
"partition.select_swapmode": "Select swap partition mode", "partition.select_swapmode": "Select swap partition mode",
"partition.use_zram": "Use zram technology", "partition.use_zram": "Use zram technology",
"partition.use_secure_boot": "Setup secure boot", "partition.use_secure_boot": "Setup secure boot",
"partition.encrypt_drive": "Encrypt drive",
"partition.current_dev_layout": "Current device layout", "partition.current_dev_layout": "Current device layout",
"partition.current_after_install": "After installation device will look like this" "partition.current_after_install": "After installation device will look like this",
"security.user_name": "User login name",
"security.user_full_name": "User full name/description",
"security.user_password": "User password",
"security.reuse_user_password": "Reuse user password for root user",
"security.root_password": "Root password",
"security.pass_low": "low security",
"security.pass_middle": "normal security",
"security.pass_hight": "hight security",
"install.caption": "Now we will beee install Bzz Linux to your PC",
"install.warning": "We a ready to start installation, this action will destroy all data on select drive!\nDo you want to proceed?"
} }
+48 -23
View File
@@ -52,6 +52,7 @@ enum Views {
Locale(stages::locale::LocaleStage), Locale(stages::locale::LocaleStage),
Keyboard(stages::keyboard::KeyboardStage), Keyboard(stages::keyboard::KeyboardStage),
Partition(stages::partition::PartitionStage), Partition(stages::partition::PartitionStage),
Security(stages::security::SecurityStage),
} }
enum Message { enum Message {
@@ -63,12 +64,14 @@ enum Message {
Locale(stages::locale::Message), Locale(stages::locale::Message),
Keyboard(stages::keyboard::Message), Keyboard(stages::keyboard::Message),
Partition(stages::partition::Message), Partition(stages::partition::Message),
Security(stages::security::Message),
} }
struct KiraState { struct KiraState {
current_view: Views, current_view: Views,
toml_config: toml::Table, toml_config: toml::Table,
config: KiraConfig, config: KiraConfig,
stages_save: Vec<Views>,
} }
const CONFIG_PATH_STR: &str = "kira_config.toml"; const CONFIG_PATH_STR: &str = "kira_config.toml";
@@ -95,6 +98,7 @@ impl KiraState {
config: KiraConfig { config: KiraConfig {
config_trail: Vec::new(), config_trail: Vec::new(),
}, },
stages_save: Vec::new(),
}, },
Task::done(Message::Start), Task::done(Message::Start),
) )
@@ -111,6 +115,7 @@ fn view(k_state: &KiraState) -> Element<'_, Message> {
Views::Locale(locale_stage) => locale_stage.view().map(Message::Locale), Views::Locale(locale_stage) => locale_stage.view().map(Message::Locale),
Views::Keyboard(keyboard_stage) => keyboard_stage.view().map(Message::Keyboard), Views::Keyboard(keyboard_stage) => keyboard_stage.view().map(Message::Keyboard),
Views::Partition(partition_stage) => partition_stage.view().map(Message::Partition), Views::Partition(partition_stage) => partition_stage.view().map(Message::Partition),
Views::Security(security_stage) => security_stage.view().map(Message::Security)
} }
} }
@@ -133,7 +138,8 @@ fn update(k_state: &mut KiraState, message: Message) -> Task<Message> {
match wlc_view.update(wlc_msg) { match wlc_view.update(wlc_msg) {
StageAction::Next(welcome_res) => { StageAction::Next(welcome_res) => {
k_state.config.config_trail.push(welcome_res); k_state.config.config_trail.push(welcome_res);
k_state.current_view = Views::License(license::LicenseStage {}); let s_state = std::mem::replace(&mut k_state.current_view, Views::License(license::LicenseStage {}));
k_state.stages_save.push(s_state);
Task::none() Task::none()
} }
StageAction::Abort => iced::exit(), StageAction::Abort => iced::exit(),
@@ -149,13 +155,13 @@ fn update(k_state: &mut KiraState, message: Message) -> Task<Message> {
match action { match action {
StageAction::Next(license_res) => { StageAction::Next(license_res) => {
k_state.config.config_trail.push(license_res); k_state.config.config_trail.push(license_res);
k_state.current_view = let s_state = std::mem::replace(&mut k_state.current_view, Views::Network(network::NetworkStage::new(&k_state.toml_config)));
Views::Network(network::NetworkStage::new(&k_state.toml_config)); k_state.stages_save.push(s_state);
Task::done(Message::Network(network::Message::CheckNetwork)) Task::done(Message::Network(network::Message::CheckNetwork))
} }
StageAction::Abort => iced::exit(), StageAction::Abort => iced::exit(),
StageAction::Back => { StageAction::Back => {
k_state.current_view = Views::Welcome(WelcomeStage::new()); k_state.current_view = k_state.stages_save.pop().unwrap();
k_state.config.config_trail.pop(); k_state.config.config_trail.pop();
Task::none() Task::none()
} }
@@ -173,12 +179,12 @@ fn update(k_state: &mut KiraState, message: Message) -> Task<Message> {
network::UpdateResult::StageAction(action) => match action { network::UpdateResult::StageAction(action) => match action {
StageAction::Next(network_res) => { StageAction::Next(network_res) => {
k_state.config.config_trail.push(network_res); k_state.config.config_trail.push(network_res);
k_state.current_view = let s_state = std::mem::replace(&mut k_state.current_view,Views::TimeZone(stages::timezone::TimeZoneStage::new()));
Views::TimeZone(stages::timezone::TimeZoneStage::new()); k_state.stages_save.push(s_state);
Task::none() Task::none()
} }
StageAction::Back => { StageAction::Back => {
k_state.current_view = Views::License(license::LicenseStage {}); k_state.current_view = k_state.stages_save.pop().unwrap();
k_state.config.config_trail.pop(); k_state.config.config_trail.pop();
Task::none() Task::none()
} }
@@ -195,16 +201,15 @@ fn update(k_state: &mut KiraState, message: Message) -> Task<Message> {
match action { match action {
StageAction::Next(tz_res) => { StageAction::Next(tz_res) => {
k_state.config.config_trail.push(tz_res); k_state.config.config_trail.push(tz_res);
k_state.current_view = let s_state = std::mem::replace(&mut k_state.current_view,Views::Locale(stages::locale::LocaleStage::new(&k_state.config)));
Views::Locale(stages::locale::LocaleStage::new(&k_state.config)); k_state.stages_save.push(s_state);
Task::none() Task::none()
} }
StageAction::Abort => iced::exit(), StageAction::Abort => iced::exit(),
StageAction::Back => { StageAction::Back => {
k_state.config.config_trail.pop(); k_state.config.config_trail.pop();
k_state.current_view = k_state.current_view = k_state.stages_save.pop().unwrap();
Views::Network(network::NetworkStage::new(&k_state.toml_config)); Task::none()
Task::done(Message::Network(network::Message::CheckNetwork))
} }
StageAction::None => Task::none(), StageAction::None => Task::none(),
} }
@@ -217,14 +222,13 @@ fn update(k_state: &mut KiraState, message: Message) -> Task<Message> {
match locale_view.update(locale_msg) { match locale_view.update(locale_msg) {
StageAction::Next(locale_res) => { StageAction::Next(locale_res) => {
k_state.config.config_trail.push(locale_res); k_state.config.config_trail.push(locale_res);
k_state.current_view = let s_state = std::mem::replace(&mut k_state.current_view,Views::Keyboard(stages::keyboard::KeyboardStage::new()));
Views::Keyboard(stages::keyboard::KeyboardStage::new()); k_state.stages_save.push(s_state);
Task::none() Task::none()
} }
StageAction::Back => { StageAction::Back => {
k_state.config.config_trail.pop(); k_state.config.config_trail.pop();
k_state.current_view = k_state.current_view = k_state.stages_save.pop().unwrap();
Views::TimeZone(stages::timezone::TimeZoneStage::new());
Task::none() Task::none()
} }
_ => Task::none(), _ => Task::none(),
@@ -240,7 +244,8 @@ fn update(k_state: &mut KiraState, message: Message) -> Task<Message> {
match stages::partition::PartitionStage::new(&k_state.toml_config) { match stages::partition::PartitionStage::new(&k_state.toml_config) {
Ok(part_stage) => { Ok(part_stage) => {
k_state.config.config_trail.push(keyboard_res); k_state.config.config_trail.push(keyboard_res);
k_state.current_view = Views::Partition(part_stage); let s_state = std::mem::replace(&mut k_state.current_view,Views::Partition(part_stage));
k_state.stages_save.push(s_state);
} }
Err(ex) => { Err(ex) => {
log::error!("Error, cannot init partition stage: {}", ex); log::error!("Error, cannot init partition stage: {}", ex);
@@ -250,8 +255,7 @@ fn update(k_state: &mut KiraState, message: Message) -> Task<Message> {
} }
StageAction::Back => { StageAction::Back => {
k_state.config.config_trail.pop(); k_state.config.config_trail.pop();
k_state.current_view = k_state.current_view = k_state.stages_save.pop().unwrap();
Views::Locale(stages::locale::LocaleStage::new(&k_state.config));
Task::none() Task::none()
} }
_ => Task::none(), _ => Task::none(),
@@ -259,18 +263,19 @@ fn update(k_state: &mut KiraState, message: Message) -> Task<Message> {
} else { } else {
Task::none() Task::none()
} }
} },
Message::Partition(partition_msg) => { Message::Partition(partition_msg) => {
if let Views::Partition(partition_view) = &mut k_state.current_view { if let Views::Partition(partition_view) = &mut k_state.current_view {
match partition_view.update(partition_msg) { match partition_view.update(partition_msg) {
StageAction::Next(partition_res) => { StageAction::Next(partition_res) => {
k_state.config.config_trail.push(partition_res); k_state.config.config_trail.push(partition_res);
iced::exit() let s_state = std::mem::replace(&mut k_state.current_view,Views::Security(stages::security::SecurityStage::new()));
k_state.stages_save.push(s_state);
Task::none()
} }
StageAction::Back => { StageAction::Back => {
k_state.config.config_trail.pop(); k_state.config.config_trail.pop();
k_state.current_view = k_state.current_view = k_state.stages_save.pop().unwrap();
Views::Keyboard(stages::keyboard::KeyboardStage::new());
Task::none() Task::none()
} }
_ => Task::none(), _ => Task::none(),
@@ -278,7 +283,27 @@ fn update(k_state: &mut KiraState, message: Message) -> Task<Message> {
} else { } else {
Task::none() Task::none()
} }
},
Message::Security(security_msg) => {
if let Views::Security(security_view) = &mut k_state.current_view {
match security_view.update(security_msg) {
StageAction::Next(security_res) => {
k_state.config.config_trail.push(security_res);
//let s_state = std::mem::replace(&mut k_state.current_view,
Task::none()
} }
StageAction::Back => {
k_state.config.config_trail.pop();
k_state.current_view = k_state.stages_save.pop().unwrap();
Task::none()
}
_ => Task::none(),
}
} else {
Task::none()
}
},
} }
} }
+95 -66
View File
@@ -18,87 +18,116 @@
This file contains basic enums and structs to be used with stages. This file contains basic enums and structs to be used with stages.
*/ */
use std::collections::HashMap; use crate::stages;
// #[derive(Debug, Clone)]
// pub enum ConfigValue {
// String(String),
// I64(i64),
// U64(u64),
// F64(f64),
// Bool(bool),
// PartLayout(PartLayout),
// KiraSize(KiraSize),
// Vector(Vec<ConfigValue>),
// Dictionary(HashMap<String, ConfigValue>),
// }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub enum ConfigValue { pub enum StageResult {
String(String), WelcomeStageResult(stages::welcome::WelcomeStageResult),
I64(i64), NetworkStageResult(stages::network::NetworkStageResult),
U64(u64), LocaleStageResult(stages::locale::LocaleStageResult),
F64(f64), TimeZoneStageResult(stages::timezone::TimeZoneStageResult),
Bool(bool), KeyboardStageResult(stages::keyboard::KeyboardStageResult),
Vector(Vec<ConfigValue>), SecurityStageResult(stages::security::SecurityStageResult),
Dictionary(HashMap<String, ConfigValue>), PartitionStageResult(stages::partition::PartitionStageResult),
} None,
#[derive(Debug, Clone)]
pub struct StageResult {
pub name: String,
pub config: Option<HashMap<String, ConfigValue>>,
pub error: Option<String>,
} }
impl StageResult { impl StageResult {
pub fn new(name: &str) -> Self { pub fn name(&self) -> String {
Self { match self {
name: name.into(), Self::LocaleStageResult(_) => "locale".into(),
config: None, Self::WelcomeStageResult(_) => "welcome".into(),
error: None, Self::NetworkStageResult(_) => "network".into(),
Self::TimeZoneStageResult(_) => "timezone".into(),
Self::KeyboardStageResult(_) => "keyboard".into(),
Self::PartitionStageResult(_) => "partition".into(),
Self::SecurityStageResult(_) => "security".into(),
Self::None => "none".into(),
} }
} }
}
// impl StageResult {
// pub fn new(name: &str) -> Self {
// Self {
// name: name.into(),
// config: None,
// error: None,
// }
// }
pub fn add_error(mut self, ex: String) -> Self { // pub fn add_error(mut self, ex: String) -> Self {
self.error = Some(ex); // self.error = Some(ex);
self // self
} // }
pub fn add_val(mut self, val_name: &str, conf_val: ConfigValue) -> Self { // pub fn add_val(mut self, val_name: &str, conf_val: ConfigValue) -> Self {
match &mut self.config { // match &mut self.config {
Some(c) => { // Some(c) => {
c.insert(val_name.into(), conf_val); // c.insert(val_name.into(), conf_val);
} // }
None => { // None => {
self.config = Some(HashMap::from([(val_name.into(), conf_val)])); // self.config = Some(HashMap::from([(val_name.into(), conf_val)]));
} // }
} // }
self // self
} // }
pub fn add_val_string(self, val_name: &str, v: String) -> Self { // pub fn add_val_string(self, val_name: &str, v: String) -> Self {
self.add_val(val_name, ConfigValue::String(v)) // self.add_val(val_name, ConfigValue::String(v))
} // }
pub fn add_val_bool(self, val_name: &str, v: bool) -> Self { // pub fn add_val_bool(self, val_name: &str, v: bool) -> Self {
self.add_val(val_name, ConfigValue::Bool(v)) // self.add_val(val_name, ConfigValue::Bool(v))
} // }
pub fn add_val_u64(self, val_name: &str, v: u64) -> Self { // pub fn add_val_u64(self, val_name: &str, v: u64) -> Self {
self.add_val(val_name, ConfigValue::U64(v)) // self.add_val(val_name, ConfigValue::U64(v))
} // }
pub fn get_val(&self, val_name: &str) -> Option<ConfigValue> { // pub fn get_val(&self, val_name: &str) -> Option<ConfigValue> {
let v = self // let v = self
.config // .config
.as_ref() // .as_ref()
.and_then(|hm| hm.get(val_name).cloned()); // .and_then(|hm| hm.get(val_name).cloned());
return v; // return v;
} // }
pub fn get_val_str(&self, val_name: &str) -> Option<String> { // pub fn get_val_str(&self, val_name: &str) -> Option<String> {
match self.get_val(val_name) { // match self.get_val(val_name) {
Some(ConfigValue::String(v)) => Some(v), // Some(ConfigValue::String(v)) => Some(v),
_ => None, // _ => None,
} // }
} // }
pub fn get_val_bool(&self, val_name: &str) -> Option<bool> { // pub fn get_val_bool(&self, val_name: &str) -> Option<bool> {
match self.get_val(val_name) { // match self.get_val(val_name) {
Some(ConfigValue::Bool(v)) => Some(v), // Some(ConfigValue::Bool(v)) => Some(v),
_ => None, // _ => None,
} // }
} // }
}
// pub fn get_val_part_layout(&self, val_name: &str) -> Option<PartLayout> {
// match self.get_val(val_name) {
// Some(ConfigValue::PartLayout(v)) => Some(v),
// _ => None,
// }
// }
// }
#[derive(Debug, Clone)]
pub struct KiraConfig { pub struct KiraConfig {
pub config_trail: Vec<StageResult>, pub config_trail: Vec<StageResult>,
} }
@@ -115,7 +144,7 @@ impl KiraConfig {
pub fn get_stage(&self, name: &str) -> Option<StageResult> { pub fn get_stage(&self, name: &str) -> Option<StageResult> {
self.config_trail self.config_trail
.iter() .iter()
.find(|v| v.name == name) .find(|v| v.name() == name)
.and_then(|v| Some(v.clone())) .and_then(|v| Some(v.clone()))
} }
pub fn pop_last(&mut self) -> Option<StageResult> { pub fn pop_last(&mut self) -> Option<StageResult> {
+217
View File
@@ -0,0 +1,217 @@
// <Kira Installer - universal Linux installer.>
// Copyright (C) <2026> <Kira Foundation>
// 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 <https://www.gnu.org/licenses/>.
/*
Actual installation script.
*/
///
/// So. I am goind to make dir structure lile this
/// /bzz/root
/// /bzz/home
/// Mount root from it and binmount home
///
use log;
use crate::{
kira_disk_layout::{self, BlkDev, PartLayout}, kira_size::KiraSize, kira_theming, stage::{KiraConfig, StageAction, StageResult}
};
use iced::{Alignment, Task, futures::{self, SinkExt}, widget};
use rust_i18n::t;
use iced::futures::channel::mpsc::{Sender};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum InstallationState {
UserConfirmDialog,
Partitionning,
CopyFiles,
Configuring,
Finish,
Error,
}
#[derive(Debug, Clone)]
pub struct InstallStage {
state: InstallationState,
progress: f32,
progress_message: String,
kira_config: KiraConfig,
}
#[derive(Debug, Clone)]
pub enum Message {
UserConfirms,
UserDenies,
StartPartitioning,
UpdateProgress(Result<(f32, String), String>),
PartitioningFinish(Result<(), String>),
Back,
Cancel,
Finish,
}
#[derive(Debug)]
pub enum Update {
StgAct(StageAction),
Task(Task<Message>),
}
fn partition_disk(disk_layout: PartLayout, password: Option<String>) -> impl futures::Stream<Item = Result<(f32, String), String>> {
use blocking;
iced::stream::channel(100, |mut output: Sender<Result<(f32, String), String>>| async move {
for i in 0..=1000 {
//sleep(Duration::from_millis(10)).await;
let dev = disk_layout.dev.clone();
// creating gpt partition table
match output.send(blocking::unblock(move || kira_disk_layout::parted_init_gpt_part_table(&dev)).await.and(Ok((1.0f32, String::new())))).await {
Err(ex) =>{
log::error!("Error creating GPT partition table: {}", ex.to_string());
return;
},
_ => ()
};
// Check if send fails (receiver dropped/cancelled)
// if output.send(res).await.is_err() {
// return; // Exit early if UI is gone
// }
}
// Optional: Send completion or error state here
// output.send(Message::WorkFailed).await.ok();
})
}
impl InstallStage {
pub fn new(config: &KiraConfig) -> Self {
Self {
state: InstallationState::UserConfirmDialog,
progress: 0.0,
progress_message: String::new(),
kira_config: config.clone(),
}
}
fn gen_result(&self) -> StageResult {
StageResult::None
}
pub fn update(&mut self, message: Message) -> Update {
match message {
Message::UserDenies => Update::StgAct(StageAction::Back),
Message::UserConfirms => {
// starting installation process here
let part_data = self.kira_config.config_trail.iter().find_map(|v| match v {
StageResult::PartitionStageResult(res) => Some(res.clone()),
_ => None,
}).unwrap();
self.state = InstallationState::Partitionning;
let disk_layout = part_data.part_layout.clone();
let disk_password = part_data.password.clone();
Update::Task(Task::run(partition_disk(disk_layout, disk_password), Message::UpdateProgress))
//Update::Task(Task::perform(, Message::PartitioningFinish))
}
Message::StartPartitioning => Update::StgAct(StageAction::None),
Message::PartitioningFinish(res) => Update::StgAct(StageAction::None),
Message::UpdateProgress(maybe_msg) => {
match maybe_msg {
Ok((progress, msg))=> {
self.progress = progress;
self.progress_message = msg;
Update::StgAct(StageAction::None)
}
Err(ex) => {
log::error!("{}", &ex);
self.state = InstallationState::Error;
Update::StgAct(StageAction::None)
}
}
//Update::StgAct(StageAction::None)
},
Message::Cancel => Update::StgAct(StageAction::Abort),
Message::Back => Update::StgAct(StageAction::Back),
Message::Finish => Update::StgAct(StageAction::None),
}
}
pub fn view(&self) -> iced::Element<'_, Message> {
let action_button = if self.state == InstallationState::Finish {
widget::button(widget::text(t!("button.finish"))).on_press(Message::Finish)
} else {
widget::button(widget::text(t!("button.cancel"))).on_press(Message::Cancel)
};
// Embed the image bytes into the executable
let welcom_logo_handle = widget::image::Handle::from_bytes(kira_theming::get_logo_bytes());
let main_content = widget::column![
widget::container(
widget::column![
widget::image(welcom_logo_handle)
.width(iced::Pixels(128.0))
.height(iced::Pixels(128.0)),
widget::text(t!("install.caption")),
widget::progress_bar(0.0..=100.0, self.progress),
widget::text(self.progress_message.clone()),
]
.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::container(action_button)
.width(iced::Length::Fill)
.align_y(Alignment::End)
.padding(10),
]
.align_x(Alignment::Center)
.spacing(10);
let page_stack = widget::Stack::with_capacity(2).push(main_content);
if self.state == InstallationState::UserConfirmDialog {
page_stack
.push(
widget::column![
widget::text(t!("install.warning")),
widget::row![
widget::button(widget::text(t!("button.no")))
.on_press(Message::UserDenies),
widget::space::horizontal(), // Pushes the right button to the far right
widget::button(widget::text(t!("button.yes")))
.on_press(Message::UserConfirms)
]
.width(iced::Length::Fill)
.align_y(Alignment::End)
.padding(10)
]
.align_x(Alignment::Center),
)
.into()
} else {
page_stack.into()
}
}
}
+14 -11
View File
@@ -18,10 +18,10 @@
This is Keyboar stage, used to select Keyboard Layouts This is Keyboar stage, used to select Keyboard Layouts
*/ */
use log;
use crate::kira_scroll_list::{self, ListViewMessage}; use crate::kira_scroll_list::{self, ListViewMessage};
use crate::stage::{StageAction, StageResult}; use crate::stage::{StageAction, StageResult};
use iced::{Alignment, Length, widget}; use iced::{Alignment, Length, widget};
use log;
use rust_i18n::t; use rust_i18n::t;
use std::collections::HashMap; use std::collections::HashMap;
@@ -124,6 +124,12 @@ pub struct KeyboardStage {
// selected_kbd_layout_id: Option<usize>, // selected_kbd_layout_id: Option<usize>,
} }
#[derive(Debug, Clone, PartialEq)]
pub struct KeyboardStageResult {
pub keyboard_model: KeyboarModel,
pub keyboard_layout: KeyboarLayout,
}
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub enum Message { pub enum Message {
SelectKeyboardModel(KeyboarModel), SelectKeyboardModel(KeyboarModel),
@@ -274,16 +280,13 @@ impl KeyboardStage {
} }
fn gen_result(&self) -> StageResult { fn gen_result(&self) -> StageResult {
if let Some(model) = &self.model StageResult::KeyboardStageResult(KeyboardStageResult {
&& let Some(k_l) = &self.keyboard_layout keyboard_model: self.model.clone().expect("Keyboard model must be choosed!"),
{ keyboard_layout: self
StageResult::new("keyboard") .keyboard_layout
.add_val_string("model", model.model.clone()) .clone()
.add_val_string("layout", k_l.to_result()) .expect("Keyboard layout must be choosed!"),
} else { })
StageResult::new("keyboard")
.add_error("Something go terrible wrong, there is no keyboard layouts.".into())
}
} }
pub fn update(&mut self, message: Message) -> StageAction { pub fn update(&mut self, message: Message) -> StageAction {
+1 -2
View File
@@ -43,8 +43,7 @@ pub enum Message {
impl LicenseStage { impl LicenseStage {
fn accepted() -> StageResult { fn accepted() -> StageResult {
StageResult::new("license") StageResult::None
.add_val_bool("accepted", true)
} }
pub fn update(&mut self, message: Message) -> stage::StageAction { pub fn update(&mut self, message: Message) -> stage::StageAction {
+22 -12
View File
@@ -20,7 +20,7 @@
use crate::{ use crate::{
kira_theming, kira_theming,
stage::{ConfigValue, KiraConfig, StageAction, StageResult}, stage::{KiraConfig, StageAction, StageResult},
}; };
use iced::{Alignment, widget}; use iced::{Alignment, widget};
use rust_i18n::t; use rust_i18n::t;
@@ -138,6 +138,12 @@ pub struct LocaleStage {
all_locale_text: String, all_locale_text: String,
} }
#[derive(Debug, Clone, PartialEq)]
pub struct LocaleStageResult {
pub lang_locale: LocaleData,
pub formats_locale: LocaleData,
}
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub enum Message { pub enum Message {
SelectLangLocale(LocaleData), SelectLangLocale(LocaleData),
@@ -149,9 +155,10 @@ pub enum Message {
impl LocaleStage { impl LocaleStage {
pub fn new(kira_config: &KiraConfig) -> Self { pub fn new(kira_config: &KiraConfig) -> Self {
// get seelctet language code from welcome stage // get seelctet language code from welcome stage
let maybe_lang = kira_config let maybe_lang = kira_config.config_trail.iter().find_map(|v| match v {
.get_stage("welcome") StageResult::WelcomeStageResult(res) => Some(res.loc_code.clone()),
.and_then(|v| v.config.and_then(|v| v.get("loc_code").cloned())); _ => None,
});
let raw_loc_codes = get_locales_codes_list_blocking().unwrap(); let raw_loc_codes = get_locales_codes_list_blocking().unwrap();
let raw_loc_descr = get_locales_description_blocking().unwrap(); let raw_loc_descr = get_locales_description_blocking().unwrap();
@@ -174,7 +181,9 @@ impl LocaleStage {
.collect(); .collect();
// if we get lang code from wellcome stage, try to fing match with system locales // if we get lang code from wellcome stage, try to fing match with system locales
let lang_match = if let Some(ConfigValue::String(lang)) = maybe_lang && lang != "en" { let lang_match = if let Some(lang) = maybe_lang
&& lang != "en"
{
let lang = lang.replace("-", "_").to_lowercase(); let lang = lang.replace("-", "_").to_lowercase();
locales locales
.iter() .iter()
@@ -208,15 +217,16 @@ impl LocaleStage {
if let Some(lang_locale) = &self.lang_locale if let Some(lang_locale) = &self.lang_locale
&& let Some(formats_locale) = &self.formats_locale && let Some(formats_locale) = &self.formats_locale
{ {
StageResult::new("locale") StageResult::LocaleStageResult(LocaleStageResult {
.add_val_string("lang_locale", lang_locale.code.clone()) lang_locale: lang_locale.clone(),
.add_val_string("formats_locale", formats_locale.code.clone()) formats_locale: formats_locale.clone(),
})
} else { } else {
let loc = LocaleData::default(); let loc = LocaleData::default();
StageResult::new("locale") StageResult::LocaleStageResult(LocaleStageResult {
.add_val_string("lang_locale", loc.code.clone()) lang_locale: loc.clone(),
.add_val_string("formats_locale", loc.code) formats_locale: loc,
})
} }
} }
+2
View File
@@ -22,3 +22,5 @@ pub mod timezone;
pub mod locale; pub mod locale;
pub mod keyboard; pub mod keyboard;
pub mod partition; pub mod partition;
pub mod security;
pub mod install;
+12 -6
View File
@@ -19,17 +19,16 @@
it needed for installation to process. it needed for installation to process.
*/ */
use log;
use iced::{Alignment, Task, widget}; use iced::{Alignment, Task, widget};
use iced_moving_picture::widget::apng; use iced_moving_picture::widget::apng;
use log;
use rust_i18n::t; use rust_i18n::t;
use toml::Table; use toml::Table;
use crate::stage; use crate::stage;
use crate::stage::StageResult; use crate::stage::StageResult;
#[derive(Debug, Clone)] #[derive(Debug, Clone, PartialEq, Eq)]
#[derive(PartialEq, Eq)]
pub enum State { pub enum State {
Cheking, Cheking,
Normal, Normal,
@@ -53,6 +52,12 @@ pub struct NetworkStage {
status_message: String, status_message: String,
} }
#[derive(Debug, Clone, PartialEq)]
pub struct NetworkStageResult {
pub internet_active: bool,
pub use_net_settings: bool,
}
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub enum Message { pub enum Message {
Next, Next,
@@ -127,9 +132,10 @@ impl NetworkStage {
} }
fn gen_result(&self) -> StageResult { fn gen_result(&self) -> StageResult {
StageResult::new("network") StageResult::NetworkStageResult(NetworkStageResult {
.add_val_bool("internet_active", self.internet_active) internet_active: self.internet_active,
.add_val_bool("use_net_settings", self.use_net_settings) use_net_settings: self.use_net_settings,
})
} }
pub fn update(&mut self, message: Message) -> UpdateResult { pub fn update(&mut self, message: Message) -> UpdateResult {
+74 -41
View File
@@ -68,6 +68,8 @@ fn part_type_to_color(t: &FSType) -> Color {
} }
} }
/// Converts PartInfo into data for ColorBar widget
///
fn part_to_ct_params(part: &PartInfo, dev_size: KiraSize) -> (String, u16, Color) { fn part_to_ct_params(part: &PartInfo, dev_size: KiraSize) -> (String, u16, Color) {
let fill_ratio: u16 = ((part.size.b() as f32 / dev_size.b() as f32) * 11.0) as u16; 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 fill_ratio = fill_ratio.max(1);
@@ -75,28 +77,50 @@ fn part_to_ct_params(part: &PartInfo, dev_size: KiraSize) -> (String, u16, Color
(part.to_string(), fill_ratio, part_color) (part.to_string(), fill_ratio, part_color)
} }
#[derive(Debug, Clone, PartialEq)]
pub struct PartitionStageResult {
pub swap_mode: SwapMode,
pub swap_size: KiraSize,
pub efi_size: KiraSize,
pub use_zram: bool,
pub secure_boot: bool,
pub encrypt_drive: bool,
pub password: Option<String>,
pub part_layout: PartLayout,
}
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct PartitionStage { pub struct PartitionStage {
device: Option<BlkDev>, device: Option<BlkDev>,
devices: Vec<BlkDev>, devices: Vec<BlkDev>,
swap_mode: Option<SwapMode>, swap_mode: Option<SwapMode>,
efi_size: KiraSize,
use_zram: bool, use_zram: bool,
secure_boot: bool, secure_boot: bool,
encrypt_drive: bool,
secure_boot_in_setup_mode: bool, secure_boot_in_setup_mode: bool,
custom_swap_size_mb: u32, custom_swap_size_mb: u32,
custom_swap_size_str: String, custom_swap_size_str: String,
selected_disk_parts: Option<Vec<(String, u16, Color)>>, selected_disk_parts: Option<Vec<(String, u16, Color)>>,
generated_disk_parts: Option<Vec<(String, u16, Color)>>, generated_disk_parts: Option<Vec<(String, u16, Color)>>,
generated_part_layout: Option<PartLayout>,
system_ram_size: KiraSize, system_ram_size: KiraSize,
vram_of_all_gpus_size: KiraSize, vram_of_all_gpus_size: KiraSize,
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub enum Message { pub enum Message {
SelectDevice(BlkDev), SelectDevice(BlkDev),
SelectSwapMode(SwapMode), SelectSwapMode(SwapMode),
ToggleZram(bool), ToggleZram(bool),
ToggleSecureBoot(bool), ToggleSecureBoot(bool),
ToggleEncrypDrive(bool),
SwapSizeFieldChange(String), SwapSizeFieldChange(String),
SwapSizeIncrement, SwapSizeIncrement,
SwapSizeDecrement, SwapSizeDecrement,
@@ -108,15 +132,24 @@ impl PartitionStage {
pub fn new(toml_config: &Table) -> Result<Self, String> { pub fn new(toml_config: &Table) -> Result<Self, String> {
let maybe_dev_list = BlkDev::list_sys_blk_dev(); let maybe_dev_list = BlkDev::list_sys_blk_dev();
log::info!("hiii from partition"); log::info!("hiii from partition");
let min_disk_size_mb = toml_config let mut min_disk_size_mb = 8192;
let mut efi_size_mb = 128;
if let Some(part_table) = toml_config
.get("partition") .get("partition")
.and_then(|v| v.as_table()) .and_then(|v| v.as_table()) {
.and_then(|v| v.get("min_disk_size_mb"))
min_disk_size_mb = part_table.get("min_disk_size_mb")
.and_then(|v| v.as_integer()) .and_then(|v| v.as_integer())
.unwrap_or(8192); .unwrap_or(8192);
let min_disk_size: KiraSize = KiraSize::new_mb(min_disk_size_mb.try_into().unwrap_or(8192)); efi_size_mb = part_table.get("efi_size_mb")
.and_then(|v| v.as_integer())
.unwrap_or(128);
}
let min_disk_size: KiraSize = KiraSize::new_mb(min_disk_size_mb.try_into().unwrap_or(8192));
let efi_size: KiraSize = KiraSize::new_mb(efi_size_mb.try_into().unwrap_or(128));
let sec_boot_setup = match kira_sysinfo::get_secure_boot_status() { let sec_boot_setup = match kira_sysinfo::get_secure_boot_status() {
Ok(sb_status) => sb_status.setup_mode, Ok(sb_status) => sb_status.setup_mode,
@@ -152,11 +185,14 @@ impl PartitionStage {
swap_mode: Some(SwapMode::SwapNoHibernate), swap_mode: Some(SwapMode::SwapNoHibernate),
use_zram: true, use_zram: true,
secure_boot: false, secure_boot: false,
encrypt_drive: false,
secure_boot_in_setup_mode: sec_boot_setup, secure_boot_in_setup_mode: sec_boot_setup,
custom_swap_size_mb: 1024, custom_swap_size_mb: 1024,
custom_swap_size_str: "1024MB".into(), custom_swap_size_str: "1024MB".into(),
efi_size: efi_size,
selected_disk_parts: None, selected_disk_parts: None,
generated_disk_parts: None, generated_disk_parts: None,
generated_part_layout:None,
system_ram_size: system_ram_size, system_ram_size: system_ram_size,
vram_of_all_gpus_size: vram_of_all_gpus_size, vram_of_all_gpus_size: vram_of_all_gpus_size,
}) })
@@ -204,35 +240,24 @@ impl PartitionStage {
fn gen_layout(&mut self) { fn gen_layout(&mut self) {
if let Some(dev) = self.device.clone() { if let Some(dev) = self.device.clone() {
let swap_size: KiraSize = self.get_swap_size(); let swap_size: KiraSize = self.get_swap_size();
self.generated_disk_parts = Some( let patr_layout = PartLayout::gen_kira_layout(dev.clone(), self.efi_size, swap_size);
PartLayout::gen_classic_layout(dev.clone(), KiraSize::new_mb(512), swap_size) self.generated_disk_parts = Some(patr_layout.part_list
.part_list
.iter() .iter()
.map(|part_info| part_to_ct_params(part_info, dev.size)) .map(|part_info| part_to_ct_params(part_info, dev.size))
.collect(), .collect(),
); );
self.generated_part_layout = Some(patr_layout);
} }
} }
fn gen_result(&self) -> StageResult { fn gen_result(&self) -> StageResult {
let swap_size: KiraSize = self.get_swap_size(); let disk_layout = self.generated_part_layout.clone().expect("Layout must be generated!");
let dev_name = self
.device StageResult::PartitionStageResult(PartitionStageResult { swap_mode: self.swap_mode.clone().unwrap(),
.clone() swap_size: self.get_swap_size(), efi_size: self.efi_size,
.expect("Device should be selected!") use_zram: self.use_zram, secure_boot: self.secure_boot, encrypt_drive: true,
.name; password: Some("123456".to_string()), part_layout: disk_layout })
StageResult::new("partition")
.add_val_string("device", dev_name)
.add_val_string(
"swap_mode",
self.swap_mode
.clone()
.unwrap_or(SwapMode::NoSwap)
.to_string(),
)
.add_val_u64("swap_size_mb", swap_size.mb())
.add_val_bool("use_zram", self.use_zram)
.add_val_bool("secure_boot", self.secure_boot)
} }
pub fn update(&mut self, message: Message) -> StageAction { pub fn update(&mut self, message: Message) -> StageAction {
@@ -252,34 +277,38 @@ impl PartitionStage {
} }
}; };
let swap_size: KiraSize = self.get_swap_size(); // let swap_size: KiraSize = self.get_swap_size();
self.generated_disk_parts = Some( // self.generated_disk_parts = Some(
PartLayout::gen_classic_layout(dev.clone(), KiraSize::new_mb(512), swap_size) // PartLayout::gen_kira_layout(dev.clone(), self.efi_size, swap_size)
.part_list // .part_list
.iter() // .iter()
.map(|part_info| part_to_ct_params(part_info, dev.size)) // .map(|part_info| part_to_ct_params(part_info, dev.size))
.collect(), // .collect(),
); // );
self.device = Some(dev); self.device = Some(dev);
self.gen_layout();
StageAction::None StageAction::None
} },
Message::SelectSwapMode(sm) => { Message::SelectSwapMode(sm) => {
self.swap_mode = Some(sm); self.swap_mode = Some(sm);
self.gen_layout(); self.gen_layout();
StageAction::None StageAction::None
} },
Message::ToggleSecureBoot(t) => { Message::ToggleSecureBoot(t) => {
self.secure_boot = t; self.secure_boot = t;
StageAction::None StageAction::None
} },
Message::ToggleEncrypDrive(t) => {
self.encrypt_drive = t;
StageAction::None
},
Message::ToggleZram(t) => { Message::ToggleZram(t) => {
self.use_zram = t; self.use_zram = t;
self.gen_layout(); self.gen_layout();
StageAction::None StageAction::None
} },
Message::SwapSizeIncrement => { Message::SwapSizeIncrement => {
self.custom_swap_size_mb = (self.custom_swap_size_mb / 1024u32) self.custom_swap_size_mb = (self.custom_swap_size_mb / 1024u32)
.strict_mul(1024u32) .strict_mul(1024u32)
@@ -287,7 +316,7 @@ impl PartitionStage {
self.custom_swap_size_str = format!("{}MB", self.custom_swap_size_mb); self.custom_swap_size_str = format!("{}MB", self.custom_swap_size_mb);
self.gen_layout(); self.gen_layout();
StageAction::None StageAction::None
} },
Message::SwapSizeDecrement => { Message::SwapSizeDecrement => {
self.custom_swap_size_mb = (self.custom_swap_size_mb / 1024u32) self.custom_swap_size_mb = (self.custom_swap_size_mb / 1024u32)
.strict_mul(1024u32) .strict_mul(1024u32)
@@ -295,7 +324,7 @@ impl PartitionStage {
self.custom_swap_size_str = format!("{}MB", self.custom_swap_size_mb); self.custom_swap_size_str = format!("{}MB", self.custom_swap_size_mb);
self.gen_layout(); self.gen_layout();
StageAction::None StageAction::None
} },
Message::SwapSizeFieldChange(f_v) => { Message::SwapSizeFieldChange(f_v) => {
let mut only_num = f_v.trim().to_string(); let mut only_num = f_v.trim().to_string();
only_num.retain(char::is_numeric); only_num.retain(char::is_numeric);
@@ -303,7 +332,7 @@ impl PartitionStage {
self.custom_swap_size_str = format!("{}MB", self.custom_swap_size_mb); self.custom_swap_size_str = format!("{}MB", self.custom_swap_size_mb);
self.gen_layout(); self.gen_layout();
StageAction::None StageAction::None
} },
Message::Back => StageAction::Back, Message::Back => StageAction::Back,
Message::Next => StageAction::Next(self.gen_result()), Message::Next => StageAction::Next(self.gen_result()),
} }
@@ -377,6 +406,10 @@ impl PartitionStage {
widget::rule::horizontal(2), widget::rule::horizontal(2),
use_sec_boot_checkbox, use_sec_boot_checkbox,
widget::rule::horizontal(2), widget::rule::horizontal(2),
widget::checkbox(self.encrypt_drive)
.label(t!("partition.encrypt_drive"))
.on_toggle(Message::ToggleEncrypDrive),
widget::rule::horizontal(2),
selected_dev_content, selected_dev_content,
dev_parts_content, dev_parts_content,
] ]
+298
View File
@@ -0,0 +1,298 @@
// <Kira Installer - universal Linux installer.>
// Copyright (C) <2026> <Kira Foundation>
// 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 <https://www.gnu.org/licenses/>.
/*
This is Security stage, setup users logins and such
*/
use crate::stage::{StageAction, StageResult};
use iced::{Alignment, Color, widget};
use rust_i18n::t;
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
enum PasswordStrenght {
Low,
Middle,
Hight,
}
impl PasswordStrenght {
pub fn from_password(password: &str) -> Self {
if password.len() < 8 {
Self::Low
} else if password.len() < 16 {
Self::Middle
} else {
Self::Hight
}
}
pub fn color(&self) -> Color {
match self {
Self::Low => Color::from_rgb8(212, 32, 32),
Self::Middle => Color::from_rgb8(212, 212, 32),
Self::Hight => Color::from_rgb8(32, 212, 32),
}
}
}
impl std::fmt::Display for PasswordStrenght {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Low => f.write_str(t!("security.pass_low").as_ref()),
Self::Middle => f.write_str(t!("security.pass_middle").as_ref()),
Self::Hight => f.write_str(t!("security.pass_hight").as_ref()),
}
}
}
#[derive(Debug, Clone)]
pub struct SecurityStage {
user_name: String,
user_full_name: String,
user_password: String,
root_password: String,
reuse_user_password_for_roor: bool,
user_pass_is_secure: bool,
root_pass_is_secure: bool,
user_password_strenght: PasswordStrenght,
root_password_strenght: PasswordStrenght,
}
#[derive(Debug, Clone)]
pub struct SecurityStageResult {
pub user_name: String,
pub user_full_name: String,
pub user_password: String,
pub root_password: String,
}
#[derive(Debug, Clone)]
pub enum Message {
ToggleReusePass(bool),
UserNameChange(String),
UserFullNameChange(String),
UserPasswordChange(String),
RootPasswordChange(String),
ToggleUserPassSecure,
ToggleRootPassSecure,
Next,
Back,
}
/// Mothod safely retain first n chars in utf-8 string avoiding cutting utf-8 char in the middle
fn retain_first_n(s: &str, n: usize) -> &str {
s.char_indices()
.nth(n)
.and_then(|(idx, _)| Some(&s[..idx]))
.unwrap_or(s)
}
impl SecurityStage {
pub fn new() -> Self {
Self {
user_name: "".into(),
user_full_name: "".into(),
user_password: "".into(),
root_password: "".into(),
reuse_user_password_for_roor: false,
user_pass_is_secure: true,
root_pass_is_secure: true,
user_password_strenght: PasswordStrenght::Low,
root_password_strenght: PasswordStrenght::Low,
}
}
fn gen_result(&self) -> StageResult {
StageResult::SecurityStageResult(SecurityStageResult {
user_name: self.user_name.clone(),
user_full_name: self.user_full_name.clone(),
user_password: self.user_password.clone(),
root_password: self.root_password.clone(),
})
}
pub fn update(&mut self, message: Message) -> StageAction {
match message {
Message::ToggleReusePass(reuse_pass) => {
self.reuse_user_password_for_roor = reuse_pass;
if reuse_pass {
self.root_password = self.user_password.clone();
}
StageAction::None
}
Message::UserNameChange(u_name) => {
let mut trimmed = u_name
.trim_start_matches(|c: char| !c.is_ascii_alphabetic())
.to_string();
trimmed.retain(|c| c.is_ascii_alphanumeric() || c == '-');
trimmed.truncate(30); // it is safe bacuse we retained only ascii characters
self.user_name = trimmed;
StageAction::None
}
Message::UserFullNameChange(f_name) => {
let mut res = f_name.clone();
res.retain(|c| c.is_alphanumeric() || ['_', '-', ' ', '.', ','].contains(&c));
self.user_full_name = retain_first_n(&res, 128).to_string();
StageAction::None
}
Message::UserPasswordChange(u_pass) => {
let mut res = u_pass;
res.retain(|c: char| !c.is_control());
self.user_password = res;
self.user_password_strenght = PasswordStrenght::from_password(&self.user_password);
if self.reuse_user_password_for_roor {
self.root_password_strenght = self.user_password_strenght;
self.root_password = self.user_password.clone();
}
StageAction::None
}
Message::RootPasswordChange(r_pass) => {
let mut res: String = r_pass;
res.retain(|c: char| !c.is_control());
self.root_password = res;
self.root_password_strenght = PasswordStrenght::from_password(&self.root_password);
StageAction::None
}
Message::ToggleUserPassSecure => {
self.user_pass_is_secure = !self.user_pass_is_secure;
StageAction::None
}
Message::ToggleRootPassSecure => {
self.root_pass_is_secure = !self.root_pass_is_secure;
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 = if self.user_name.is_empty()
|| self.user_password.is_empty()
|| self.root_password.is_empty()
{
widget::button(widget::text(t!("button.next")))
} else {
widget::button(widget::text(t!("button.next"))).on_press(Message::Next)
};
let user_pass_sec_text = if self.user_pass_is_secure {
widget::text("👁")
} else {
widget::text("*")
};
let root_pass_sec_text = if self.root_pass_is_secure {
widget::text("👁")
} else {
widget::text("*")
};
let user_pass_sec_text = user_pass_sec_text
.width(12)
.align_x(Alignment::Center)
.align_y(Alignment::Center);
let root_pass_sec_text = root_pass_sec_text
.width(12)
.align_x(Alignment::Center)
.align_y(Alignment::Center);
let mut root_pass_input =
widget::text_input("", &self.root_password).secure(self.root_pass_is_secure);
if !self.reuse_user_password_for_roor {
root_pass_input = root_pass_input.on_input(Message::RootPasswordChange);
}
let user_p_strenght_text = widget::text(self.user_password_strenght.to_string())
.color(self.user_password_strenght.color());
let root_p_strenght_text = widget::text(self.root_password_strenght.to_string())
.color(self.root_password_strenght.color());
widget::column![
widget::container(
widget::column![
widget::column![
widget::text(t!("security.user_name")),
widget::text_input("", &self.user_name)
.width(256)
.on_input(Message::UserNameChange)
],
widget::column![
widget::text(t!("security.user_full_name")),
widget::text_input("", &self.user_full_name)
.width(256)
.on_input(Message::UserFullNameChange)
],
widget::column![
widget::text(t!("security.user_password")),
widget::row![
widget::text_input("", &self.user_password)
.width(512)
.on_input(Message::UserPasswordChange)
.secure(self.user_pass_is_secure),
widget::button(user_pass_sec_text)
.on_press(Message::ToggleUserPassSecure),
widget::container(user_p_strenght_text)
.align_x(Alignment::Center)
.style(widget::container::bordered_box)
.style(widget::container::rounded_box)
.padding(6)
.width(160),
]
.spacing(5)
.align_y(Alignment::Center)
],
widget::rule::horizontal(2),
widget::column![
widget::text(t!("security.root_password")),
widget::row![
root_pass_input.width(512),
widget::button(root_pass_sec_text)
.on_press(Message::ToggleRootPassSecure),
widget::container(root_p_strenght_text)
.align_x(Alignment::Center)
.style(widget::container::rounded_box)
.style(widget::container::bordered_box)
.padding(6)
.width(160),
]
.spacing(5)
.align_y(Alignment::Center),
widget::checkbox(self.reuse_user_password_for_roor)
.label(t!("security.reuse_user_password"))
.on_toggle(Message::ToggleReusePass),
],
]
.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()
}
}
+17 -10
View File
@@ -18,9 +18,9 @@
This is TimeZone stage, used to select timezone This is TimeZone stage, used to select timezone
*/ */
use log;
use crate::stage::{StageAction, StageResult}; use crate::stage::{StageAction, StageResult};
use iced::{Alignment, Length, widget}; use iced::{Alignment, Length, widget};
use log;
use rust_i18n::t; use rust_i18n::t;
use std::collections::HashMap; use std::collections::HashMap;
@@ -60,6 +60,11 @@ pub struct TimeZoneStage {
selected_zone_text: String, selected_zone_text: String,
} }
#[derive(Debug, Clone)]
pub struct TimeZoneStageResult {
pub time_zone: TimeZoneData,
}
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub enum Message { pub enum Message {
SelectRegion(kira_scroll_list::ListViewMessage), SelectRegion(kira_scroll_list::ListViewMessage),
@@ -146,14 +151,12 @@ impl TimeZoneStage {
} }
fn gen_result(&self) -> StageResult { fn gen_result(&self) -> StageResult {
if let Some(time_zone) = &self.selected_zone { StageResult::TimeZoneStageResult(TimeZoneStageResult {
StageResult::new("time_zone") time_zone: self
.add_val_string("name", time_zone.to_string()) .selected_zone
.add_val_string("region", time_zone.region.clone()) .clone()
.add_val_string("zone", time_zone.zone.clone()) .expect("Timezone must be selected!"),
} else { })
StageResult::new("time_zone")
}
} }
pub fn update(&mut self, message: Message) -> StageAction { pub fn update(&mut self, message: Message) -> StageAction {
@@ -201,7 +204,11 @@ impl TimeZoneStage {
widget::row![ widget::row![
kira_scroll_list::list_view(&self.regions, self.region_id, Length::Shrink) kira_scroll_list::list_view(&self.regions, self.region_id, Length::Shrink)
.map(Message::SelectRegion), .map(Message::SelectRegion),
kira_scroll_list::list_view(&self.zones, self.zone_id, Length::FillPortion(1)) kira_scroll_list::list_view(
&self.zones,
self.zone_id,
Length::FillPortion(1)
)
.map(Message::SelectZone), .map(Message::SelectZone),
] ]
.padding([0, 10]) .padding([0, 10])
+19 -12
View File
@@ -14,13 +14,14 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
/* /*
This is Welcome stage, used to greet used and allow user to choose program language This is Welcome stage, used to greet used and allow user to choose program language
*/ */
use crate::{
use crate::{stage::{StageAction, StageResult}, kira_theming}; kira_theming,
stage::{StageAction, StageResult},
};
use iced::{Alignment, widget}; use iced::{Alignment, widget};
use rust_i18n::t; use rust_i18n::t;
@@ -42,6 +43,12 @@ pub struct WelcomeStage {
locales: Vec<LocData>, locales: Vec<LocData>,
} }
#[derive(Debug, Clone, PartialEq)]
pub struct WelcomeStageResult {
pub loc_code: String,
pub loc_name: String,
}
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub enum Message { pub enum Message {
SelectLocale(LocData), SelectLocale(LocData),
@@ -51,7 +58,6 @@ pub enum Message {
impl WelcomeStage { impl WelcomeStage {
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
locale: Some(LocData { locale: Some(LocData {
name: "English".into(), name: "English".into(),
@@ -69,13 +75,15 @@ impl WelcomeStage {
fn gen_result(&self) -> StageResult { fn gen_result(&self) -> StageResult {
if let Some(loc) = &self.locale { if let Some(loc) = &self.locale {
StageResult::new("welcome") StageResult::WelcomeStageResult(WelcomeStageResult {
.add_val_string("loc_code", loc.code.clone()) loc_code: loc.code.clone(),
.add_val_string("loc_name", loc.name.clone()) loc_name: loc.name.clone(),
})
} else { } else {
StageResult::new("welcome") StageResult::WelcomeStageResult(WelcomeStageResult {
.add_val_string("loc_code", "en".to_string()) loc_code: "en".to_string(),
.add_val_string("loc_name", "English".to_string()) loc_name: "English".to_string(),
})
} }
} }
@@ -96,8 +104,7 @@ impl WelcomeStage {
let exit_button = widget::button(widget::text(t!("button.exit"))).on_press(Message::Exit); let exit_button = widget::button(widget::text(t!("button.exit"))).on_press(Message::Exit);
// Embed the image bytes into the executable // Embed the image bytes into the executable
let welcom_logo_handle = let welcom_logo_handle = widget::image::Handle::from_bytes(kira_theming::get_logo_bytes());
widget::image::Handle::from_bytes(kira_theming::get_logo_bytes());
widget::column![ widget::column![
widget::container( widget::container(