SHA256
Compare commits
15
Commits
| Author | SHA256 | Date | |
|---|---|---|---|
|
|
b5b5bd764a | ||
|
|
3b086605cf | ||
|
|
fa5c3d3aec | ||
|
|
dda9288e23 | ||
|
|
f404ff385a | ||
|
|
c56da21eab | ||
|
|
ed6ffcdee1 | ||
|
|
4d8c9540d0 | ||
|
|
56e87a62cf | ||
|
|
9769ae409e | ||
|
|
3abbbe9abd | ||
|
|
b2f510e679 | ||
|
|
a503b2a36f | ||
|
|
f0a8e704c0 | ||
|
|
187129d314 |
Generated
+480
-605
File diff suppressed because it is too large
Load Diff
+9
-2
@@ -9,17 +9,19 @@ edition = "2024"
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
blocking = "1.6"
|
blocking = "1.6"
|
||||||
iced = { version = "0.14", default-features = false, features = ["crisp", "linux-theme-detection", "tiny-skia", "x11", "smol", "image"] }
|
iced = { version = "0.14", default-features = false, features = ["crisp", "linux-theme-detection", "tiny-skia", "x11", "smol", "image"] }
|
||||||
|
#iced = { git = "https://github.com/iced-rs/iced", branch = "vello_cpu", default-features = false, features = ["crisp", "linux-theme-detection", "vello-cpu", "x11", "smol", "image"]}
|
||||||
iced_moving_picture = "0"
|
iced_moving_picture = "0"
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
rust-i18n = "3"
|
rust-i18n = "3"
|
||||||
|
serde = { version = "1", features = ["derive"] }
|
||||||
|
serde_json = "1"
|
||||||
time-format = "1.2"
|
time-format = "1.2"
|
||||||
toml = "1"
|
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
rand = "0.10"
|
rand = "0.10"
|
||||||
|
|
||||||
[profile.dev]
|
[profile.dev]
|
||||||
opt-level = 2
|
opt-level = 3
|
||||||
codegen-units = 16
|
codegen-units = 16
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
@@ -27,6 +29,11 @@ lto = true
|
|||||||
debug = false
|
debug = false
|
||||||
incremental = false
|
incremental = false
|
||||||
codegen-units = 1
|
codegen-units = 1
|
||||||
|
# We need level 3 for tiny-skia otherwise render is lagging
|
||||||
|
# opt-level = "s"
|
||||||
opt-level = 3
|
opt-level = 3
|
||||||
strip = true
|
strip = true
|
||||||
|
|
||||||
|
|
||||||
|
[registry]
|
||||||
|
global-min-publish-age = "14 days"
|
||||||
|
|||||||
@@ -4,3 +4,4 @@ reqire_network = true
|
|||||||
|
|
||||||
[partition]
|
[partition]
|
||||||
min_disk_size_mb = 8192
|
min_disk_size_mb = 8192
|
||||||
|
efi_size_mb = 128
|
||||||
|
|||||||
+144
-37
@@ -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,36 @@ 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)]
|
||||||
|
pub struct BcachefsProp {
|
||||||
|
pub password: Option<String>,
|
||||||
|
pub comp_alg: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BcachefsProp {
|
||||||
|
pub fn new(password: Option<String>, comp_alg: Option<String>) -> Self {
|
||||||
|
Self {
|
||||||
|
password: password,
|
||||||
|
comp_alg: comp_alg,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
password: None,
|
||||||
|
comp_alg: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
@@ -106,7 +146,7 @@ pub enum FSType {
|
|||||||
SWAP,
|
SWAP,
|
||||||
EXT4,
|
EXT4,
|
||||||
BTRFS,
|
BTRFS,
|
||||||
BCACHEFS,
|
BCACHEFS(BcachefsProp),
|
||||||
UNKNOWN,
|
UNKNOWN,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -119,7 +159,7 @@ impl std::fmt::Display for FSType {
|
|||||||
Self::SWAP => write!(f, "swap"),
|
Self::SWAP => write!(f, "swap"),
|
||||||
Self::EXT4 => write!(f, "ext4"),
|
Self::EXT4 => write!(f, "ext4"),
|
||||||
Self::BTRFS => write!(f, "btrfs"),
|
Self::BTRFS => write!(f, "btrfs"),
|
||||||
Self::BCACHEFS => write!(f, "bcachefs"),
|
Self::BCACHEFS(_) => write!(f, "bcachefs"),
|
||||||
Self::UNKNOWN => write!(f, "unknown"),
|
Self::UNKNOWN => write!(f, "unknown"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -135,10 +175,26 @@ impl FSType {
|
|||||||
"swap" => Self::SWAP,
|
"swap" => Self::SWAP,
|
||||||
"ext4" => Self::EXT4,
|
"ext4" => Self::EXT4,
|
||||||
"btrfs" => Self::BTRFS,
|
"btrfs" => Self::BTRFS,
|
||||||
"bcachefs" => Self::BCACHEFS,
|
"bcachefs" => Self::BCACHEFS(BcachefsProp::default()),
|
||||||
_ => 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(_) => "xfs",
|
||||||
|
// 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 {
|
||||||
@@ -157,10 +213,7 @@ impl FSType {
|
|||||||
pub enum PartRole {
|
pub enum PartRole {
|
||||||
EFI,
|
EFI,
|
||||||
ROOT,
|
ROOT,
|
||||||
HOME,
|
|
||||||
SWAP,
|
SWAP,
|
||||||
LuksRoot,
|
|
||||||
LuksHome,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
@@ -177,17 +230,27 @@ 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
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PartInfo {
|
impl PartInfo {
|
||||||
|
///
|
||||||
|
/// calculate par end byte as 0 starting bytes index (for parted and such)
|
||||||
|
///
|
||||||
|
pub fn part_end(&self) -> KiraSize {
|
||||||
|
// important to shift here by 1 because disk index is starting from 0, and part end pointing to last byte of the partition.
|
||||||
|
(self.start + self.size) - KiraSize::new_b(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Returns zero based byte index of start of next (right after this one) partition
|
||||||
|
///
|
||||||
|
pub fn next_part_start(&self) -> KiraSize {
|
||||||
|
// important to shift here by 1 because disk index is starting from 0, and part end pointing to last byte of the partition.
|
||||||
|
self.start + self.size
|
||||||
|
}
|
||||||
|
|
||||||
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(),
|
||||||
@@ -206,6 +269,12 @@ impl PartInfo {
|
|||||||
role: None,
|
role: None,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn partlabel_dev(&self) -> Option<String> {
|
||||||
|
self.gpt_label
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|l| Some(format!("/dev/disk/by-partlabel/{}", l)))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// getting list of device partitions
|
/// getting list of device partitions
|
||||||
@@ -214,9 +283,11 @@ fn get_dev_part_blocking(dev_name: &str) -> Result<Vec<PartInfo>, String> {
|
|||||||
|
|
||||||
//"lsblk -o NAME,SIZE,FSTYPE -l -n /dev/nvme0n1"
|
//"lsblk -o NAME,SIZE,FSTYPE -l -n /dev/nvme0n1"
|
||||||
// lsblk -o NAME,SIZE,FSTYPE,TYPE,PARTLABEL,LABEL,PARTFLAGS -b -P
|
// lsblk -o NAME,SIZE,FSTYPE,TYPE,PARTLABEL,LABEL,PARTFLAGS -b -P
|
||||||
|
// -x NAME - sorting by name
|
||||||
match Command::new("lsblk")
|
match Command::new("lsblk")
|
||||||
.args([
|
.args([
|
||||||
|
"-x",
|
||||||
|
"NAME",
|
||||||
"-o",
|
"-o",
|
||||||
"NAME,SIZE,FSTYPE,TYPE,PARTLABEL,LABEL,MOUNTPOINT,PARTFLAGS",
|
"NAME,SIZE,FSTYPE,TYPE,PARTLABEL,LABEL,MOUNTPOINT,PARTFLAGS",
|
||||||
"-b",
|
"-b",
|
||||||
@@ -283,7 +354,7 @@ pub fn align_part(size: u64, start: u64, align: u64) -> Option<(u64, u64, u64)>
|
|||||||
// decreasing end if not align
|
// decreasing end if not align
|
||||||
let part_end = align_start + size;
|
let part_end = align_start + size;
|
||||||
let end_mod = part_end % align;
|
let end_mod = part_end % align;
|
||||||
log::debug!("part_end: {} end_mod: {}", part_end, end_mod);
|
|
||||||
let align_end = part_end - end_mod - 1;
|
let align_end = part_end - end_mod - 1;
|
||||||
|
|
||||||
let align_size = align_end - align_start + 1;
|
let align_size = align_end - align_start + 1;
|
||||||
@@ -291,24 +362,39 @@ 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;
|
||||||
}
|
}
|
||||||
|
log::debug!(
|
||||||
|
"align_size: {}, align_start:{}, align_end: {} end_mod: {}",
|
||||||
|
align_size,
|
||||||
|
align_start,
|
||||||
|
align_end,
|
||||||
|
end_mod
|
||||||
|
);
|
||||||
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,
|
||||||
pub part_list: Vec<PartInfo>,
|
pub part_list: Vec<PartInfo>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// GPT requires 33x512 sectors at the very end of the disk (16896 bytes):
|
||||||
|
/// 32 sectors - backup Partition Entry Array
|
||||||
|
/// 1 sector - backup GPT header
|
||||||
|
///
|
||||||
impl PartLayout {
|
impl PartLayout {
|
||||||
pub fn new(dev: BlkDev) -> Self {
|
pub fn new(dev: BlkDev) -> Self {
|
||||||
|
// 16896 - reserved space at the dev end for gpt
|
||||||
|
let usable_space = dev.size - KiraSize::new_b(16896);
|
||||||
Self {
|
Self {
|
||||||
empty_space: dev.size,
|
empty_space: usable_space,
|
||||||
dev: dev,
|
dev: dev,
|
||||||
part_list: Vec::new(),
|
part_list: Vec::new(),
|
||||||
}
|
}
|
||||||
@@ -316,7 +402,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,
|
||||||
@@ -324,11 +412,11 @@ impl PartLayout {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_table(mut self) -> Self {
|
// pub fn new_table(mut self) -> Self {
|
||||||
self.empty_space = self.dev.size;
|
// self.empty_space = self.dev.size;
|
||||||
self.part_list = Vec::new();
|
// self.part_list = Vec::new();
|
||||||
self
|
// self
|
||||||
}
|
// }
|
||||||
|
|
||||||
pub fn add_part(
|
pub fn add_part(
|
||||||
mut self,
|
mut self,
|
||||||
@@ -340,19 +428,29 @@ impl PartLayout {
|
|||||||
role: Option<PartRole>,
|
role: Option<PartRole>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let part_start = if self.part_list.is_empty() {
|
let part_start = if self.part_list.is_empty() {
|
||||||
|
// need to substract 2 mb offset from disk start.
|
||||||
|
self.empty_space = self.empty_space - KiraSize::new_mb(2);
|
||||||
KiraSize::new_mb(2)
|
KiraSize::new_mb(2)
|
||||||
} else {
|
} else {
|
||||||
self.dev.size - self.empty_space
|
// last part end + 1
|
||||||
|
self.part_list.last().unwrap().next_part_start()
|
||||||
};
|
};
|
||||||
|
|
||||||
let (size, start, _) =
|
let (a_size, a_start, _) =
|
||||||
align_part(part_size.b(), part_start.b(), self.dev.sector_size.b()).unwrap();
|
align_part(part_size.b(), part_start.b(), self.dev.sector_size.b()).unwrap();
|
||||||
|
|
||||||
|
log::debug!(
|
||||||
|
"PartLayout.add_part gpt_label{:?}, size_asked:{}, size_al:{}",
|
||||||
|
gpt_label,
|
||||||
|
part_size,
|
||||||
|
a_size
|
||||||
|
);
|
||||||
|
|
||||||
let part_name = format!("{}p{}", self.dev.name, self.part_list.len() + 1);
|
let part_name = format!("{}p{}", self.dev.name, self.part_list.len() + 1);
|
||||||
self.part_list.push(PartInfo {
|
self.part_list.push(PartInfo {
|
||||||
name: part_name,
|
name: part_name,
|
||||||
size: KiraSize::new_b(size),
|
size: KiraSize::new_b(a_size),
|
||||||
start: KiraSize::new_b(start),
|
start: KiraSize::new_b(a_start),
|
||||||
fs_type: fs_type,
|
fs_type: fs_type,
|
||||||
gpt_label: gpt_label,
|
gpt_label: gpt_label,
|
||||||
fs_label: fs_label,
|
fs_label: fs_label,
|
||||||
@@ -360,10 +458,14 @@ impl PartLayout {
|
|||||||
role: role,
|
role: role,
|
||||||
});
|
});
|
||||||
|
|
||||||
self.empty_space = self.empty_space - KiraSize::new_b(size);
|
self.empty_space = self.empty_space - KiraSize::new_b(a_size);
|
||||||
|
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
///
|
||||||
|
/// Use this to add partition of (disk_empty_space - reserve_space)
|
||||||
|
/// Specifically to leave some space at the end for SWAP and such
|
||||||
|
///
|
||||||
pub fn add_part_reserve(
|
pub fn add_part_reserve(
|
||||||
self,
|
self,
|
||||||
reserve_space: KiraSize,
|
reserve_space: KiraSize,
|
||||||
@@ -377,7 +479,13 @@ 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,
|
||||||
|
maybe_password: Option<String>,
|
||||||
|
maybe_compr: Option<String>,
|
||||||
|
) -> Self {
|
||||||
let mut res: PartLayout = PartLayout::new(dev);
|
let mut res: PartLayout = PartLayout::new(dev);
|
||||||
|
|
||||||
// UEFI partition
|
// UEFI partition
|
||||||
@@ -385,15 +493,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("/beefi".into()),
|
||||||
Some(PartRole::EFI),
|
Some(PartRole::EFI),
|
||||||
)
|
)
|
||||||
.add_part_reserve(
|
.add_part_reserve(
|
||||||
swap_size,
|
swap_size,
|
||||||
FSType::XFS,
|
FSType::BCACHEFS(BcachefsProp::new(maybe_password, maybe_compr)),
|
||||||
Some("root".into()),
|
Some("bee-root".into()),
|
||||||
None,
|
None,
|
||||||
Some("/".into()),
|
Some("/".into()),
|
||||||
Some(PartRole::ROOT),
|
Some(PartRole::ROOT),
|
||||||
@@ -404,7 +512,7 @@ impl PartLayout {
|
|||||||
res = res.add_part(
|
res = res.add_part(
|
||||||
swap_size,
|
swap_size,
|
||||||
FSType::SWAP,
|
FSType::SWAP,
|
||||||
Some("swap".into()),
|
Some("bee-swap".into()),
|
||||||
None,
|
None,
|
||||||
Some("SWAP".into()),
|
Some("SWAP".into()),
|
||||||
Some(PartRole::SWAP),
|
Some(PartRole::SWAP),
|
||||||
@@ -466,4 +574,3 @@ mod tests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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 }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -95,3 +95,37 @@ pub fn get_gpus_list() -> Vec<GpuInfo> {
|
|||||||
|
|
||||||
res
|
res
|
||||||
}
|
}
|
||||||
|
|
||||||
|
use serde::Deserialize;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Deserialize)]
|
||||||
|
pub struct SecureBootStatus {
|
||||||
|
pub installed: bool,
|
||||||
|
pub guid: String,
|
||||||
|
pub setup_mode: bool,
|
||||||
|
pub secure_boot: bool,
|
||||||
|
pub vendors: Vec<String>,
|
||||||
|
pub firmware_quirks: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_secure_boot_status() -> Result<SecureBootStatus, String> {
|
||||||
|
use std::process::Command;
|
||||||
|
|
||||||
|
//sbctl --json status
|
||||||
|
match Command::new("sbctl").args(["--json", "status"]).output() {
|
||||||
|
Ok(cmd_output) => {
|
||||||
|
if cmd_output.status.success() {
|
||||||
|
match serde_json::from_slice(&cmd_output.stdout) {
|
||||||
|
Ok(res) => Ok(res),
|
||||||
|
Err(ex) => Err(ex.to_string()),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Err(format!(
|
||||||
|
"Error getting disk devices list: {}",
|
||||||
|
String::from_utf8(cmd_output.stderr).unwrap_or("UNKNOWN".into())
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(ex) => Err(ex.to_string()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,332 @@
|
|||||||
|
// <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/>.
|
||||||
|
|
||||||
|
/*
|
||||||
|
Functions for creating with disk layout
|
||||||
|
*/
|
||||||
|
|
||||||
|
use crate::kira_disk_layout::*;
|
||||||
|
use crate::kira_size::KiraSize;
|
||||||
|
use std::fs;
|
||||||
|
use std::process::Command;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct CommandFailed {
|
||||||
|
stderr: Vec<u8>,
|
||||||
|
cmd: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for CommandFailed {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"Command failed! Command: {}; Stderr: {}",
|
||||||
|
self.cmd,
|
||||||
|
String::from_utf8(self.stderr.clone())
|
||||||
|
.unwrap_or_else(|_| { "STDEER IS NOT VALID UTF-8".to_string() })
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CommandFailed {
|
||||||
|
pub fn new(cmd: &str, args: &Vec<&str>, stderr: Vec<u8>) -> Self {
|
||||||
|
Self {
|
||||||
|
stderr: stderr,
|
||||||
|
cmd: format!("{} {}", cmd, args.join(" ")),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum ExecCommandError {
|
||||||
|
IoError(std::io::Error),
|
||||||
|
FromUtf8Error(std::string::FromUtf8Error),
|
||||||
|
CommandFailed(CommandFailed),
|
||||||
|
Other(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Implement From for each wrapped error type
|
||||||
|
impl From<std::io::Error> for ExecCommandError {
|
||||||
|
fn from(err: std::io::Error) -> Self {
|
||||||
|
ExecCommandError::IoError(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<std::string::FromUtf8Error> for ExecCommandError {
|
||||||
|
fn from(err: std::string::FromUtf8Error) -> Self {
|
||||||
|
ExecCommandError::FromUtf8Error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<CommandFailed> for ExecCommandError {
|
||||||
|
fn from(err: CommandFailed) -> Self {
|
||||||
|
ExecCommandError::CommandFailed(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<String> for ExecCommandError {
|
||||||
|
fn from(err: String) -> Self {
|
||||||
|
ExecCommandError::Other(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for ExecCommandError {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
ExecCommandError::IoError(e) => write!(f, "IO Error: {}", e),
|
||||||
|
ExecCommandError::FromUtf8Error(e) => {
|
||||||
|
write!(f, "IO streem to String parse Error: {}", e)
|
||||||
|
}
|
||||||
|
ExecCommandError::CommandFailed(ex) => {
|
||||||
|
write!(f, "{}", ex)
|
||||||
|
}
|
||||||
|
ExecCommandError::Other(msg) => write!(f, "Error: {}", msg),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Implement std::error::Error
|
||||||
|
impl std::error::Error for ExecCommandError {}
|
||||||
|
|
||||||
|
/// exec given sommand with arguments, capturing stderr in case or failure
|
||||||
|
fn exec_command(cmd: &str, args: &Vec<&str>) -> Result<(), ExecCommandError> {
|
||||||
|
let out = Command::new(cmd).args(args).output()?;
|
||||||
|
if out.status.success() {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(CommandFailed::new(cmd, args, out.stderr).into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// exec given sommand with arguments, returning stdout parsed to UTF-8 string
|
||||||
|
/// capturing stderr in case or failure
|
||||||
|
fn exec_command_out(cmd: &str, args: &Vec<&str>) -> Result<String, ExecCommandError> {
|
||||||
|
let out = Command::new(cmd).args(args).output()?;
|
||||||
|
if out.status.success() {
|
||||||
|
Ok(String::from_utf8(out.stdout)?)
|
||||||
|
} else {
|
||||||
|
Err(CommandFailed::new(cmd, args, out.stderr).into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// sync system time with chrony
|
||||||
|
/// dinitctl start chrony
|
||||||
|
pub fn dinit_sync_time_chrony() -> Result<(), ExecCommandError> {
|
||||||
|
exec_command("dinitctl", &vec!["start", "chrony"])
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Install base system
|
||||||
|
/// basestrap /mnt base base-devel dinit elogind-dinit
|
||||||
|
pub fn basestrap_dinit(root_dir: &str) -> Result<(), ExecCommandError> {
|
||||||
|
exec_command(
|
||||||
|
"basestrap",
|
||||||
|
&vec![root_dir, "base", "base-devel", "dinit", "elogind-dinit"],
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Executes parted command with given arguments
|
||||||
|
fn exec_parted(args: &Vec<&str>) -> Result<String, ExecCommandError> {
|
||||||
|
exec_command_out("parted", args)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parted_get_last_part_num(dev: &BlkDev) -> Result<u32, ExecCommandError> {
|
||||||
|
// parted -m /dev/loop0 print
|
||||||
|
let res = exec_parted(&vec!["-m", dev.full_name().as_str(), "print"])?;
|
||||||
|
|
||||||
|
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().into()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// init gpt partition table
|
||||||
|
/// parted --script $install_device mklabel gpt
|
||||||
|
/// Its blocking function
|
||||||
|
pub fn parted_init_gpt_part_table(dev: &BlkDev) -> Result<(), ExecCommandError> {
|
||||||
|
let dev_full_name = dev.full_name();
|
||||||
|
let args = vec!["--script", &dev_full_name, "mklabel", "gpt"];
|
||||||
|
exec_command("parted", &args)
|
||||||
|
}
|
||||||
|
|
||||||
|
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<(), ExecCommandError> {
|
||||||
|
// important to shift here by 1 because disk index is starting from 0, and part end pointing to last byte of the partition.
|
||||||
|
let part_end = part.part_end();
|
||||||
|
|
||||||
|
// gpt label should be in double qutes
|
||||||
|
let gpt_label = if let Some(s) = &part.gpt_label {
|
||||||
|
format!("\"{}\"", s)
|
||||||
|
} else {
|
||||||
|
"\"\"".to_string()
|
||||||
|
};
|
||||||
|
log::debug!(
|
||||||
|
"Creatin partition dev:{}, label:{}, fs_type:{}, start:{}, end:{}",
|
||||||
|
dev.full_name(),
|
||||||
|
gpt_label,
|
||||||
|
part.fs_type.to_parted_str(),
|
||||||
|
part.start.to_parted_bytes_str(),
|
||||||
|
part_end.to_parted_bytes_str()
|
||||||
|
);
|
||||||
|
// 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
|
||||||
|
/// compression: lz4, zstd
|
||||||
|
pub fn bcachefs_format(
|
||||||
|
dev_name: &str,
|
||||||
|
compression: Option<String>,
|
||||||
|
password: Option<String>,
|
||||||
|
) -> Result<(), ExecCommandError> {
|
||||||
|
// 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
|
||||||
|
|
||||||
|
let mut arg = vec!["format", "--force"];
|
||||||
|
// if compression algorythm specified
|
||||||
|
let maybe_comp_cmd = compression.and_then(|alg| Some(format!("--compression={}", alg)));
|
||||||
|
if let Some(comp_cmd) = &maybe_comp_cmd {
|
||||||
|
arg.push(comp_cmd);
|
||||||
|
}
|
||||||
|
// if we want encrypted volume
|
||||||
|
if let Some(passw) = password {
|
||||||
|
// creating tmp file with password
|
||||||
|
fs::write("/tmp/bzzpsspass.txt", &passw)?;
|
||||||
|
arg.push("--encrypted");
|
||||||
|
arg.push("--passphrase_file=/tmp/bzzpsspass.txt");
|
||||||
|
}
|
||||||
|
|
||||||
|
// add dev name at the end
|
||||||
|
arg.push(dev_name);
|
||||||
|
|
||||||
|
let out = exec_command("bcachefs", &arg);
|
||||||
|
|
||||||
|
// ignore any errors while deleting pass tmp file
|
||||||
|
let _ = fs::remove_file("/tmp/bzzpsspass.txt");
|
||||||
|
|
||||||
|
out
|
||||||
|
}
|
||||||
|
|
||||||
|
/// unlok encrypted bcache volume
|
||||||
|
pub fn bcachefs_unlock(dev_name: &str, pass: &str) -> Result<(), ExecCommandError> {
|
||||||
|
// bcachefs unlock --file=/tmp/bzzpsspass.txt /dev/loop0
|
||||||
|
let arg = vec!["unlock", "--file=/tmp/bzzpsspass.txt ", dev_name];
|
||||||
|
// creating tmp file with password
|
||||||
|
fs::write("/tmp/bzzpsspass.txt", &pass)?;
|
||||||
|
|
||||||
|
let out = exec_command("bcachefs", &arg);
|
||||||
|
// ignore any errors while deleting pass tmp file
|
||||||
|
let _ = fs::remove_file("/tmp/bzzpsspass.txt");
|
||||||
|
|
||||||
|
out
|
||||||
|
}
|
||||||
|
|
||||||
|
/// create subvolume
|
||||||
|
pub fn bcachefs_create_subvolume(vol_path: &str) -> Result<(), ExecCommandError> {
|
||||||
|
// sudo bcachefs subvolume create /mnt/test/nyan
|
||||||
|
exec_command("bcachefs", &vec!["subvolume", "create", vol_path])
|
||||||
|
}
|
||||||
|
|
||||||
|
/// mount bcachefs
|
||||||
|
pub fn bcachefs_mount(dev_name: &str, mount_point: &str) -> Result<(), ExecCommandError> {
|
||||||
|
// sudo mount -t bcachefs /dev/loop0 /mnt/test
|
||||||
|
exec_command("mount", &vec!["-t", "bcachefs", dev_name, mount_point])
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn mount_fs(dev_name: &str, mount_point: &str) -> Result<(), ExecCommandError> {
|
||||||
|
// sudo mount -t bcachefs /dev/loop0 /mnt/test
|
||||||
|
exec_command("mount", &vec![dev_name, mount_point])
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn swapon(dev_name: &str) -> Result<(), ExecCommandError> {
|
||||||
|
exec_command("swapon", &vec![dev_name])
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub fn fatlabel(dev: &str, label: &str) -> Result<(), ExecCommandError> {
|
||||||
|
//fatlabel /dev/sda1 ESP
|
||||||
|
exec_command("fatlabel", &vec![dev, label])
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn mkfs_fat32(dev: &str, maybe_label: Option<&str>) -> Result<(), ExecCommandError> {
|
||||||
|
//mkfs.fat -F 32 /dev/sda1
|
||||||
|
exec_command("mkfs.fat", &vec!["-F", "32", dev])?;
|
||||||
|
|
||||||
|
// if need to set label
|
||||||
|
if let Some(label) = maybe_label {
|
||||||
|
fatlabel(dev, label)
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// mkfs.fat -F 32 /dev/sda1
|
||||||
|
/// fatlabel /dev/sda1 ESP
|
||||||
|
pub fn mkfs_esp(dev: &str) -> Result<(), ExecCommandError> {
|
||||||
|
mkfs_fat32(dev, Some("ESP"))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn mkswap(dev: &str) -> Result<(), ExecCommandError> {
|
||||||
|
//mkswap -L SWAP /dev/sda4
|
||||||
|
exec_command("mkswap", &vec!["-L", "bee-swap", dev])
|
||||||
|
}
|
||||||
@@ -20,6 +20,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
use iced::{Color, Theme, color};
|
use iced::{Color, Theme, color};
|
||||||
|
use iced::widget::container;
|
||||||
|
|
||||||
// Function returns theme to be used by installer
|
// Function returns theme to be used by installer
|
||||||
pub fn main_theme() -> Theme {
|
pub fn main_theme() -> Theme {
|
||||||
@@ -38,6 +39,20 @@ pub fn change_lightnes(c: &Color, factor: f32) -> Color {
|
|||||||
res
|
res
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub fn dialog_background(_theme: &Theme) -> container::Style {
|
||||||
|
let mut back = _theme.palette().background.clone();
|
||||||
|
back.a = 0.8;
|
||||||
|
container::Style {
|
||||||
|
background: Some(iced::Background::Color(back)),
|
||||||
|
..container::Style::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn warning_dialog(_theme: &Theme) -> container::Style {
|
||||||
|
container::bordered_box(_theme).background(color!(0xDB5375))
|
||||||
|
}
|
||||||
|
|
||||||
// pub fn text_with_border(_theme: &Theme) -> container::Style {
|
// pub fn text_with_border(_theme: &Theme) -> container::Style {
|
||||||
|
|
||||||
// container::Style {
|
// container::Style {
|
||||||
|
|||||||
+15
-2
@@ -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!\n\nDo you want to proceed?"
|
||||||
}
|
}
|
||||||
+115
-63
@@ -35,6 +35,7 @@ use crate::stages::welcome::WelcomeStage;
|
|||||||
rust_i18n::i18n!("src/locales", fallback = "en");
|
rust_i18n::i18n!("src/locales", fallback = "en");
|
||||||
mod kira_color_bar;
|
mod kira_color_bar;
|
||||||
mod kira_disk_layout;
|
mod kira_disk_layout;
|
||||||
|
mod kira_system;
|
||||||
mod kira_logger;
|
mod kira_logger;
|
||||||
mod kira_scroll_list;
|
mod kira_scroll_list;
|
||||||
mod kira_size;
|
mod kira_size;
|
||||||
@@ -52,6 +53,8 @@ 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),
|
||||||
|
Install(stages::install::InstallStage),
|
||||||
}
|
}
|
||||||
|
|
||||||
enum Message {
|
enum Message {
|
||||||
@@ -63,38 +66,38 @@ 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),
|
||||||
|
Install(stages::install::Message),
|
||||||
}
|
}
|
||||||
|
|
||||||
struct KiraState {
|
struct KiraState {
|
||||||
current_view: Views,
|
current_view: Views,
|
||||||
toml_config: toml::Table,
|
k_config: KiraConfig,
|
||||||
config: KiraConfig,
|
stages_save: Vec<Views>,
|
||||||
}
|
}
|
||||||
|
|
||||||
const CONFIG_PATH_STR: &str = "kira_config.toml";
|
// const CONFIG_PATH_STR: &str = "kira_config.toml";
|
||||||
|
|
||||||
fn load_kira_config(config_path: &str) -> Result<toml::Table, Box<dyn std::error::Error>> {
|
// fn load_kira_config(config_path: &str) -> Result<toml::Table, Box<dyn std::error::Error>> {
|
||||||
use std::fs;
|
// use std::fs;
|
||||||
use toml::Table;
|
// use toml::Table;
|
||||||
// 1. Read the file content into a String
|
// // 1. Read the file content into a String
|
||||||
let content = fs::read_to_string(config_path)?;
|
// let content = fs::read_to_string(config_path)?;
|
||||||
|
|
||||||
// 2. Parse the string into a Table
|
// // 2. Parse the string into a Table
|
||||||
let table: Table = content.parse()?;
|
// let table: Table = content.parse()?;
|
||||||
|
|
||||||
log::info!("{:?}", table);
|
// log::info!("{:?}", table);
|
||||||
Ok(table)
|
// Ok(table)
|
||||||
}
|
// }
|
||||||
|
|
||||||
impl KiraState {
|
impl KiraState {
|
||||||
fn boot() -> (Self, Task<Message>) {
|
fn boot() -> (Self, Task<Message>) {
|
||||||
(
|
(
|
||||||
Self {
|
Self {
|
||||||
current_view: Views::Start,
|
current_view: Views::Start,
|
||||||
toml_config: toml::Table::new(),
|
k_config: KiraConfig ::default(),
|
||||||
config: KiraConfig {
|
stages_save: Vec::new(),
|
||||||
config_trail: Vec::new(),
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
Task::done(Message::Start),
|
Task::done(Message::Start),
|
||||||
)
|
)
|
||||||
@@ -111,29 +114,28 @@ 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),
|
||||||
|
Views::Install(install_stage) => install_stage.view().map(Message::Install),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update(k_state: &mut KiraState, message: Message) -> Task<Message> {
|
fn update(k_state: &mut KiraState, message: Message) -> Task<Message> {
|
||||||
match message {
|
match message {
|
||||||
Message::Start => match load_kira_config(CONFIG_PATH_STR) {
|
Message::Start => {
|
||||||
Ok(conf) => {
|
log::info!("Starting!");
|
||||||
log::info!("Config loaded!");
|
k_state.current_view = Views::Welcome(WelcomeStage::new(k_state.k_config.clone()));
|
||||||
k_state.toml_config = conf;
|
|
||||||
k_state.current_view = Views::Welcome(WelcomeStage::new());
|
|
||||||
Task::none()
|
Task::none()
|
||||||
}
|
|
||||||
Err(ex) => {
|
|
||||||
log::error!("Error reading config {}: {}", CONFIG_PATH_STR, ex);
|
|
||||||
iced::exit()
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
Message::Welcome(wlc_msg) => {
|
Message::Welcome(wlc_msg) => {
|
||||||
if let Views::Welcome(wlc_view) = &mut k_state.current_view {
|
if let Views::Welcome(wlc_view) = &mut k_state.current_view {
|
||||||
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.k_config = 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::new(k_state.k_config.clone())),
|
||||||
|
);
|
||||||
|
k_state.stages_save.push(s_state);
|
||||||
Task::none()
|
Task::none()
|
||||||
}
|
}
|
||||||
StageAction::Abort => iced::exit(),
|
StageAction::Abort => iced::exit(),
|
||||||
@@ -148,15 +150,17 @@ fn update(k_state: &mut KiraState, message: Message) -> Task<Message> {
|
|||||||
let action = license_view.update(license_message);
|
let action = license_view.update(license_message);
|
||||||
match action {
|
match action {
|
||||||
StageAction::Next(license_res) => {
|
StageAction::Next(license_res) => {
|
||||||
k_state.config.config_trail.push(license_res);
|
k_state.k_config = license_res;
|
||||||
k_state.current_view =
|
let s_state = std::mem::replace(
|
||||||
Views::Network(network::NetworkStage::new(&k_state.toml_config));
|
&mut k_state.current_view,
|
||||||
|
Views::Network(network::NetworkStage::new(k_state.k_config.clone())),
|
||||||
|
);
|
||||||
|
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();
|
|
||||||
Task::none()
|
Task::none()
|
||||||
}
|
}
|
||||||
StageAction::None => Task::none(),
|
StageAction::None => Task::none(),
|
||||||
@@ -172,14 +176,16 @@ fn update(k_state: &mut KiraState, message: Message) -> Task<Message> {
|
|||||||
network::UpdateResult::Task(t) => t.map(Message::Network),
|
network::UpdateResult::Task(t) => t.map(Message::Network),
|
||||||
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.k_config = network_res;
|
||||||
k_state.current_view =
|
let s_state = std::mem::replace(
|
||||||
Views::TimeZone(stages::timezone::TimeZoneStage::new());
|
&mut k_state.current_view,
|
||||||
|
Views::TimeZone(stages::timezone::TimeZoneStage::new(k_state.k_config.clone())),
|
||||||
|
);
|
||||||
|
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();
|
|
||||||
Task::none()
|
Task::none()
|
||||||
}
|
}
|
||||||
_ => Task::none(),
|
_ => Task::none(),
|
||||||
@@ -194,17 +200,18 @@ fn update(k_state: &mut KiraState, message: Message) -> Task<Message> {
|
|||||||
let action = timezone_view.update(timezone_message);
|
let action = timezone_view.update(timezone_message);
|
||||||
match action {
|
match action {
|
||||||
StageAction::Next(tz_res) => {
|
StageAction::Next(tz_res) => {
|
||||||
k_state.config.config_trail.push(tz_res);
|
k_state.k_config = tz_res;
|
||||||
k_state.current_view =
|
let s_state = std::mem::replace(
|
||||||
Views::Locale(stages::locale::LocaleStage::new(&k_state.config));
|
&mut k_state.current_view,
|
||||||
|
Views::Locale(stages::locale::LocaleStage::new(k_state.k_config.clone())),
|
||||||
|
);
|
||||||
|
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.current_view = k_state.stages_save.pop().unwrap();
|
||||||
k_state.current_view =
|
Task::none()
|
||||||
Views::Network(network::NetworkStage::new(&k_state.toml_config));
|
|
||||||
Task::done(Message::Network(network::Message::CheckNetwork))
|
|
||||||
}
|
}
|
||||||
StageAction::None => Task::none(),
|
StageAction::None => Task::none(),
|
||||||
}
|
}
|
||||||
@@ -216,15 +223,16 @@ fn update(k_state: &mut KiraState, message: Message) -> Task<Message> {
|
|||||||
if let Views::Locale(locale_view) = &mut k_state.current_view {
|
if let Views::Locale(locale_view) = &mut k_state.current_view {
|
||||||
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.k_config = locale_res;
|
||||||
k_state.current_view =
|
let s_state = std::mem::replace(
|
||||||
Views::Keyboard(stages::keyboard::KeyboardStage::new());
|
&mut k_state.current_view,
|
||||||
|
Views::Keyboard(stages::keyboard::KeyboardStage::new(k_state.k_config.clone())),
|
||||||
|
);
|
||||||
|
k_state.stages_save.push(s_state);
|
||||||
Task::none()
|
Task::none()
|
||||||
}
|
}
|
||||||
StageAction::Back => {
|
StageAction::Back => {
|
||||||
k_state.config.config_trail.pop();
|
k_state.current_view = k_state.stages_save.pop().unwrap();
|
||||||
k_state.current_view =
|
|
||||||
Views::TimeZone(stages::timezone::TimeZoneStage::new());
|
|
||||||
Task::none()
|
Task::none()
|
||||||
}
|
}
|
||||||
_ => Task::none(),
|
_ => Task::none(),
|
||||||
@@ -237,10 +245,14 @@ fn update(k_state: &mut KiraState, message: Message) -> Task<Message> {
|
|||||||
if let Views::Keyboard(keyboard_view) = &mut k_state.current_view {
|
if let Views::Keyboard(keyboard_view) = &mut k_state.current_view {
|
||||||
match keyboard_view.update(keyboard_msg) {
|
match keyboard_view.update(keyboard_msg) {
|
||||||
StageAction::Next(keyboard_res) => {
|
StageAction::Next(keyboard_res) => {
|
||||||
match stages::partition::PartitionStage::new(&k_state.toml_config) {
|
k_state.k_config = keyboard_res;
|
||||||
|
match stages::partition::PartitionStage::new(k_state.k_config.clone()) {
|
||||||
Ok(part_stage) => {
|
Ok(part_stage) => {
|
||||||
k_state.config.config_trail.push(keyboard_res);
|
let s_state = std::mem::replace(
|
||||||
k_state.current_view = Views::Partition(part_stage);
|
&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);
|
||||||
@@ -249,9 +261,7 @@ fn update(k_state: &mut KiraState, message: Message) -> Task<Message> {
|
|||||||
Task::none()
|
Task::none()
|
||||||
}
|
}
|
||||||
StageAction::Back => {
|
StageAction::Back => {
|
||||||
k_state.config.config_trail.pop();
|
k_state.current_view = k_state.stages_save.pop().unwrap();
|
||||||
k_state.current_view =
|
|
||||||
Views::Locale(stages::locale::LocaleStage::new(&k_state.config));
|
|
||||||
Task::none()
|
Task::none()
|
||||||
}
|
}
|
||||||
_ => Task::none(),
|
_ => Task::none(),
|
||||||
@@ -264,13 +274,16 @@ fn update(k_state: &mut KiraState, message: Message) -> Task<Message> {
|
|||||||
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.k_config = partition_res;
|
||||||
iced::exit()
|
let s_state = std::mem::replace(
|
||||||
|
&mut k_state.current_view,
|
||||||
|
Views::Security(stages::security::SecurityStage::new(k_state.k_config.clone())),
|
||||||
|
);
|
||||||
|
k_state.stages_save.push(s_state);
|
||||||
|
Task::none()
|
||||||
}
|
}
|
||||||
StageAction::Back => {
|
StageAction::Back => {
|
||||||
k_state.config.config_trail.pop();
|
k_state.current_view = k_state.stages_save.pop().unwrap();
|
||||||
k_state.current_view =
|
|
||||||
Views::Keyboard(stages::keyboard::KeyboardStage::new());
|
|
||||||
Task::none()
|
Task::none()
|
||||||
}
|
}
|
||||||
_ => Task::none(),
|
_ => Task::none(),
|
||||||
@@ -279,6 +292,45 @@ fn update(k_state: &mut KiraState, message: Message) -> Task<Message> {
|
|||||||
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.k_config = security_res;
|
||||||
|
let s_state = std::mem::replace(
|
||||||
|
&mut k_state.current_view,
|
||||||
|
Views::Install(stages::install::InstallStage::new(k_state.k_config.clone())),
|
||||||
|
);
|
||||||
|
k_state.stages_save.push(s_state);
|
||||||
|
Task::none()
|
||||||
|
}
|
||||||
|
StageAction::Back => {
|
||||||
|
k_state.current_view = k_state.stages_save.pop().unwrap();
|
||||||
|
Task::none()
|
||||||
|
}
|
||||||
|
_ => Task::none(),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Task::none()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Message::Install(install_msg) => {
|
||||||
|
if let Views::Install(install_view) = &mut k_state.current_view {
|
||||||
|
let update_result = install_view.update(install_msg);
|
||||||
|
match update_result {
|
||||||
|
stages::install::Update::Task(t) => t.map(Message::Install),
|
||||||
|
stages::install::Update::StgAct(stage_action) => match stage_action {
|
||||||
|
StageAction::Back => {
|
||||||
|
k_state.current_view = k_state.stages_save.pop().unwrap();
|
||||||
|
Task::none()
|
||||||
|
}
|
||||||
|
_ => Task::none(),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Task::none()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 251 KiB After Width: | Height: | Size: 9.1 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 56 KiB After Width: | Height: | Size: 9.1 KiB |
+21
-92
@@ -18,114 +18,43 @@
|
|||||||
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)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum ConfigValue {
|
|
||||||
String(String),
|
|
||||||
I64(i64),
|
|
||||||
U64(u64),
|
|
||||||
F64(f64),
|
|
||||||
Bool(bool),
|
|
||||||
Vector(Vec<ConfigValue>),
|
|
||||||
Dictionary(HashMap<String, ConfigValue>),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct StageResult {
|
|
||||||
pub name: String,
|
|
||||||
pub config: Option<HashMap<String, ConfigValue>>,
|
|
||||||
pub error: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
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 {
|
|
||||||
self.error = Some(ex);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn add_val(mut self, val_name: &str, conf_val: ConfigValue) -> Self {
|
|
||||||
match &mut self.config {
|
|
||||||
Some(c) => {
|
|
||||||
c.insert(val_name.into(), conf_val);
|
|
||||||
}
|
|
||||||
None => {
|
|
||||||
self.config = Some(HashMap::from([(val_name.into(), conf_val)]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn add_val_string(self, val_name: &str, v: String) -> Self {
|
|
||||||
self.add_val(val_name, ConfigValue::String(v))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn add_val_bool(self, val_name: &str, v: bool) -> Self {
|
|
||||||
self.add_val(val_name, ConfigValue::Bool(v))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_val(&self, val_name: &str) -> Option<ConfigValue> {
|
|
||||||
let v = self
|
|
||||||
.config
|
|
||||||
.as_ref()
|
|
||||||
.and_then(|hm| hm.get(val_name).cloned());
|
|
||||||
return v;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_val_str(&self, val_name: &str) -> Option<String> {
|
|
||||||
match self.get_val(val_name) {
|
|
||||||
Some(ConfigValue::String(v)) => Some(v),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_val_bool(&self, val_name: &str) -> Option<bool> {
|
|
||||||
match self.get_val(val_name) {
|
|
||||||
Some(ConfigValue::Bool(v)) => Some(v),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct KiraConfig {
|
pub struct KiraConfig {
|
||||||
pub config_trail: Vec<StageResult>,
|
pub res_welcome: Option<stages::welcome::WelcomeStageResult>,
|
||||||
|
pub res_license: bool,
|
||||||
|
pub res_network: Option<stages::network::NetworkStageResult>,
|
||||||
|
pub res_timezone: Option<stages::timezone::TimeZoneStageResult>,
|
||||||
|
pub res_locale: Option<stages::locale::LocaleStageResult>,
|
||||||
|
pub res_keyboard: Option<stages::keyboard::KeyboardStageResult>,
|
||||||
|
pub res_partition: Option<stages::partition::PartitionStageResult>,
|
||||||
|
pub res_security: Option<stages::security::SecurityStageResult>,
|
||||||
|
pub res_install: Option<stages::install::InstallStageResult>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for KiraConfig {
|
impl Default for KiraConfig {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
config_trail: Vec::new(),
|
res_welcome: None,
|
||||||
|
res_license: false,
|
||||||
|
res_network: None,
|
||||||
|
res_timezone: None,
|
||||||
|
res_locale: None,
|
||||||
|
res_keyboard: None,
|
||||||
|
res_partition: None,
|
||||||
|
res_security: None,
|
||||||
|
res_install: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl KiraConfig {
|
|
||||||
pub fn get_stage(&self, name: &str) -> Option<StageResult> {
|
|
||||||
self.config_trail
|
|
||||||
.iter()
|
|
||||||
.find(|v| v.name == name)
|
|
||||||
.and_then(|v| Some(v.clone()))
|
|
||||||
}
|
|
||||||
pub fn pop_last(&mut self) -> Option<StageResult> {
|
|
||||||
self.config_trail.pop()
|
|
||||||
}
|
|
||||||
pub fn push(&mut self, stage_result: StageResult) {
|
|
||||||
self.config_trail.push(stage_result);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum StageAction {
|
pub enum StageAction {
|
||||||
Back,
|
Back,
|
||||||
None,
|
None,
|
||||||
Next(StageResult),
|
Next(KiraConfig),
|
||||||
Abort,
|
Abort,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,370 @@
|
|||||||
|
// <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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
kira_disk_layout::{self, BlkDev, FSType, PartLayout, PartRole},
|
||||||
|
kira_size::KiraSize,
|
||||||
|
kira_system::{self, ExecCommandError},
|
||||||
|
kira_theming,
|
||||||
|
stage::{KiraConfig, StageAction},
|
||||||
|
stages::partition::PartitionStageResult,
|
||||||
|
};
|
||||||
|
use iced::{
|
||||||
|
Alignment, Task,
|
||||||
|
futures::{self, SinkExt, StreamExt, TryStreamExt, channel::mpsc::SendError},
|
||||||
|
widget,
|
||||||
|
};
|
||||||
|
use iced::{Color, Length, futures::channel::mpsc::Sender, widget::container};
|
||||||
|
|
||||||
|
use log;
|
||||||
|
use rust_i18n::t;
|
||||||
|
|
||||||
|
const EFI_BOOT_MOUNT_POINT: &str = "/beefi";
|
||||||
|
const MNT_DIR: &str = "/mnt/inst_root";
|
||||||
|
const ROOT_PARTLABEL_DEV: &str = "/dev/disk/by-partlabel/bee-root";
|
||||||
|
|
||||||
|
/// Creates new GPT table and partitions from PartLayout
|
||||||
|
pub fn create_partitions(part_layout: &PartLayout) -> Result<(), ExecCommandError> {
|
||||||
|
log::info!("Creating GPT part table...");
|
||||||
|
kira_system::parted_init_gpt_part_table(&part_layout.dev)?;
|
||||||
|
log::info!("Start to create partitions...");
|
||||||
|
for part in &part_layout.part_list {
|
||||||
|
log::info!("Creating partition: {}", part);
|
||||||
|
kira_system::parted_mkpart(&part_layout.dev, part)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// inits bcachefs volume for installation to use
|
||||||
|
/// Formats device to bcachefs
|
||||||
|
/// Unlock it if encryption used
|
||||||
|
/// Mount to specified dir
|
||||||
|
/// creates subvolumes bzz/root bzz/home
|
||||||
|
pub fn bcachefs_init(
|
||||||
|
dev_name: &str,
|
||||||
|
mnt_dir: &str,
|
||||||
|
password: Option<String>,
|
||||||
|
) -> Result<(), ExecCommandError> {
|
||||||
|
// if encryption used - unlock drive
|
||||||
|
if let Some(pass) = password {
|
||||||
|
kira_system::bcachefs_unlock(dev_name, pass.as_str())?;
|
||||||
|
}
|
||||||
|
|
||||||
|
//mount drive
|
||||||
|
kira_system::bcachefs_mount(dev_name, mnt_dir)?;
|
||||||
|
|
||||||
|
// Creating bzz-snaps directory for root snapshoots
|
||||||
|
std::fs::create_dir(format!("{}{}", mnt_dir, "/bzz-snaps"))?;
|
||||||
|
|
||||||
|
// Creating boot directory for efi mounting
|
||||||
|
std::fs::create_dir_all(format!("{}{}", mnt_dir, EFI_BOOT_MOUNT_POINT))?;
|
||||||
|
|
||||||
|
// create subvolumes
|
||||||
|
for vol_name in ["/home", "/usr", "/var", "/var/tmp"] {
|
||||||
|
let vol_path = format!("{}{}", mnt_dir, vol_name);
|
||||||
|
kira_system::bcachefs_create_subvolume(vol_path.as_str())?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn init_disk(part_layout: &PartLayout, mnt_dir: &str) -> Result<(), ExecCommandError> {
|
||||||
|
|
||||||
|
|
||||||
|
let mnt_path = Path::new(mnt_dir);
|
||||||
|
log::info!("Cheking is MNT path exists: {}", mnt_dir);
|
||||||
|
if !mnt_path.exists() {
|
||||||
|
log::info!("MNT path do not exist, creating it...");
|
||||||
|
std::fs::create_dir_all(mnt_path)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
// use parted to create partition gpt layout
|
||||||
|
create_partitions(part_layout)?;
|
||||||
|
|
||||||
|
// format partitions
|
||||||
|
for part in &part_layout.part_list {
|
||||||
|
match part.role {
|
||||||
|
Some(PartRole::EFI) => {
|
||||||
|
if let Some(part_l) = part.partlabel_dev() {
|
||||||
|
kira_system::mkfs_esp(&part_l)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Some(PartRole::ROOT) => {
|
||||||
|
if let Some(part_l) = part.partlabel_dev() {
|
||||||
|
if let FSType::BCACHEFS(bc_par) = &part.fs_type {
|
||||||
|
log::info!("Formating root pertition.");
|
||||||
|
kira_system::bcachefs_format(&part_l, bc_par.password.clone(), bc_par.password.clone())?;
|
||||||
|
// init root
|
||||||
|
log::info!("Mount and init root partition.");
|
||||||
|
bcachefs_init(ROOT_PARTLABEL_DEV, mnt_dir, bc_par.password.clone())?;
|
||||||
|
} else {
|
||||||
|
return Err(ExecCommandError::Other(
|
||||||
|
"ROOT partition is not BCACHEFS type!".into(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Some(PartRole::SWAP) => {
|
||||||
|
if let Some(part_l) = part.partlabel_dev() {
|
||||||
|
kira_system::mkswap(&part_l)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
enum InstallationState {
|
||||||
|
UserConfirmDialog,
|
||||||
|
Installing,
|
||||||
|
Finish,
|
||||||
|
Error,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct InstallStageResult {
|
||||||
|
pub sucess: Result<(), String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct InstallStage {
|
||||||
|
state: InstallationState,
|
||||||
|
progress: f32,
|
||||||
|
progress_message: String,
|
||||||
|
modal_backgroud: Color,
|
||||||
|
k_config: KiraConfig,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum Message {
|
||||||
|
UserConfirms,
|
||||||
|
UserDenies,
|
||||||
|
Sucess,
|
||||||
|
Cancel,
|
||||||
|
Finish,
|
||||||
|
UpdateProgress(Result<(f32, String), String>),
|
||||||
|
Failure(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum Update {
|
||||||
|
StgAct(StageAction),
|
||||||
|
Task(Task<Message>),
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn init_disk_async(part_layout: PartLayout, mnt_dir: String) -> Result<(), ExecCommandError> {
|
||||||
|
use blocking;
|
||||||
|
blocking::unblock(move || init_disk(&part_layout, &mnt_dir)).await
|
||||||
|
}
|
||||||
|
|
||||||
|
fn do_install(
|
||||||
|
k_config: KiraConfig,
|
||||||
|
) -> impl futures::Stream<Item = Result<(f32, String), ExecCommandError>> {
|
||||||
|
iced::stream::channel(
|
||||||
|
100,
|
||||||
|
|mut output: Sender<Result<(f32, String), ExecCommandError>>| async move {
|
||||||
|
//sleep(Duration::from_millis(10)).await;
|
||||||
|
let disk_layout = k_config.res_partition.unwrap().part_layout.clone();
|
||||||
|
|
||||||
|
// creating gpt partition table
|
||||||
|
let res = init_disk_async(disk_layout, MNT_DIR.into()).await;
|
||||||
|
let res_err = res.is_err();
|
||||||
|
output
|
||||||
|
.send(res.map(|_| (1.1f32, "Formatting bcachefs".into())))
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
if res_err {
|
||||||
|
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();
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_kconfig(k_config: &KiraConfig) -> Result<(), String> {
|
||||||
|
if k_config.res_welcome.is_none(){ Err("Missing wellcome stage result!".into())}
|
||||||
|
else if k_config.res_license == false { Err("License is NOT accepted!".into())}
|
||||||
|
else if k_config.res_locale.is_none(){ Err("Missing locale stage result!".into())}
|
||||||
|
else if k_config.res_network.is_none(){ Err("Missing network stage result!".into())}
|
||||||
|
else if k_config.res_keyboard.is_none(){ Err("Missing keyboard stage result!".into())}
|
||||||
|
else if k_config.res_timezone.is_none(){ Err("Missing timezone stage result!".into())}
|
||||||
|
else if k_config.res_partition.is_none(){ Err("Missing partition stage result!".into())}
|
||||||
|
else if k_config.res_security.is_none(){ Err("Missing security stage result!".into())}
|
||||||
|
else {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl InstallStage {
|
||||||
|
pub fn new(k_config: KiraConfig) -> Self {
|
||||||
|
let mut modal_backgroud = kira_theming::main_theme().palette().background;
|
||||||
|
modal_backgroud.a = 0.5;
|
||||||
|
Self {
|
||||||
|
modal_backgroud: modal_backgroud,
|
||||||
|
state: InstallationState::UserConfirmDialog,
|
||||||
|
progress: 0.0,
|
||||||
|
progress_message: String::new(),
|
||||||
|
k_config: k_config,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn gen_result(&mut self) -> KiraConfig {
|
||||||
|
self.k_config.res_install = Some(InstallStageResult { sucess: Ok(()) });
|
||||||
|
self.k_config.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update(&mut self, message: Message) -> Update {
|
||||||
|
match message {
|
||||||
|
Message::UserDenies => Update::StgAct(StageAction::Back),
|
||||||
|
Message::UserConfirms => {
|
||||||
|
|
||||||
|
log::info!("Cheking config...");
|
||||||
|
match check_kconfig(&self.k_config) {
|
||||||
|
Ok(()) => log::info!("Config looks good."),
|
||||||
|
Err(ex) => {
|
||||||
|
log::error!("{}", ex);
|
||||||
|
self.state = InstallationState::Error;
|
||||||
|
return Update::StgAct(StageAction::None);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// starting installation process here
|
||||||
|
log::info!("Starting disk partitioning.");
|
||||||
|
|
||||||
|
self.state = InstallationState::Installing;
|
||||||
|
Update::Task(Task::run(
|
||||||
|
do_install(self.k_config.clone()).map_err(|ex| ex.to_string()),
|
||||||
|
Message::UpdateProgress,
|
||||||
|
))
|
||||||
|
//Update::Task(Task::perform(, Message::PartitioningFinish))
|
||||||
|
}
|
||||||
|
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::Failure(ex) => {
|
||||||
|
self.state = InstallationState::Error;
|
||||||
|
Update::StgAct(StageAction::Abort)
|
||||||
|
}
|
||||||
|
Message::Cancel => Update::StgAct(StageAction::Abort),
|
||||||
|
Message::Sucess => Update::StgAct(StageAction::None),
|
||||||
|
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 logo_handle = widget::image::Handle::from_bytes(kira_theming::get_logo_bytes());
|
||||||
|
|
||||||
|
let main_content = widget::column![
|
||||||
|
widget::container(
|
||||||
|
widget::column![
|
||||||
|
widget::image(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::opaque(
|
||||||
|
container(
|
||||||
|
container(
|
||||||
|
widget::column![
|
||||||
|
widget::text(t!("install.warning")).align_x(Alignment::Center),
|
||||||
|
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)
|
||||||
|
]
|
||||||
|
.padding(10)
|
||||||
|
]
|
||||||
|
.spacing(10)
|
||||||
|
.padding(10)
|
||||||
|
.width(512)
|
||||||
|
.align_x(Alignment::Center),
|
||||||
|
)
|
||||||
|
.style(kira_theming::warning_dialog),
|
||||||
|
)
|
||||||
|
.style(container::bordered_box)
|
||||||
|
.style(kira_theming::dialog_background)
|
||||||
|
.align_x(Alignment::Center)
|
||||||
|
.align_y(Alignment::Center)
|
||||||
|
.height(Length::Fill)
|
||||||
|
.width(Length::Fill),
|
||||||
|
))
|
||||||
|
.into()
|
||||||
|
} else {
|
||||||
|
page_stack.into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+21
-15
@@ -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::{KiraConfig, StageAction};
|
||||||
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;
|
||||||
|
|
||||||
@@ -119,9 +119,15 @@ pub struct KeyboardStage {
|
|||||||
variants_map: HashMap<String, Vec<Variant>>,
|
variants_map: HashMap<String, Vec<Variant>>,
|
||||||
variants: Vec<Variant>,
|
variants: Vec<Variant>,
|
||||||
variant: Option<usize>,
|
variant: Option<usize>,
|
||||||
|
|
||||||
keyboard_layout: Option<KeyboarLayout>,
|
keyboard_layout: Option<KeyboarLayout>,
|
||||||
// selected_kbd_layout_id: Option<usize>,
|
// selected_kbd_layout_id: Option<usize>,
|
||||||
|
k_config: KiraConfig,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
pub struct KeyboardStageResult {
|
||||||
|
pub keyboard_model: KeyboarModel,
|
||||||
|
pub keyboard_layout: KeyboarLayout,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
@@ -224,7 +230,7 @@ fn get_layouts_blocking() -> std::io::Result<(
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl KeyboardStage {
|
impl KeyboardStage {
|
||||||
pub fn new() -> Self {
|
pub fn new(k_config: KiraConfig) -> Self {
|
||||||
match get_layouts_blocking() {
|
match get_layouts_blocking() {
|
||||||
Ok((models, layouts, variants_map)) => {
|
Ok((models, layouts, variants_map)) => {
|
||||||
let def_layout = layouts
|
let def_layout = layouts
|
||||||
@@ -252,6 +258,7 @@ impl KeyboardStage {
|
|||||||
layout: def_layout,
|
layout: def_layout,
|
||||||
variant: Some(0),
|
variant: Some(0),
|
||||||
keyboard_layout: keyboard_layout,
|
keyboard_layout: keyboard_layout,
|
||||||
|
k_config: k_config,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(ex) => {
|
Err(ex) => {
|
||||||
@@ -268,22 +275,21 @@ impl KeyboardStage {
|
|||||||
layout: None,
|
layout: None,
|
||||||
variant: None,
|
variant: None,
|
||||||
keyboard_layout: None,
|
keyboard_layout: None,
|
||||||
|
k_config: k_config,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn gen_result(&self) -> StageResult {
|
fn gen_result(&mut self) -> KiraConfig {
|
||||||
if let Some(model) = &self.model
|
self.k_config.res_keyboard = Some(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")
|
self.k_config.clone()
|
||||||
.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 {
|
||||||
|
|||||||
@@ -23,10 +23,12 @@ use iced::{Alignment, widget};
|
|||||||
use rust_i18n::t;
|
use rust_i18n::t;
|
||||||
|
|
||||||
use crate::stage;
|
use crate::stage;
|
||||||
use crate::stage::StageResult;
|
use crate::stage::KiraConfig;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct LicenseStage {}
|
pub struct LicenseStage {
|
||||||
|
k_config: KiraConfig,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum Message {
|
pub enum Message {
|
||||||
@@ -42,14 +44,18 @@ pub enum Message {
|
|||||||
//const LICENSE_TEXT: &str = include_str!("gpl-3.0.txt");
|
//const LICENSE_TEXT: &str = include_str!("gpl-3.0.txt");
|
||||||
|
|
||||||
impl LicenseStage {
|
impl LicenseStage {
|
||||||
fn accepted() -> StageResult {
|
pub fn new(k_config: KiraConfig) -> Self {
|
||||||
StageResult::new("license")
|
Self { k_config: k_config }
|
||||||
.add_val_bool("accepted", true)
|
}
|
||||||
|
|
||||||
|
fn accepted(&mut self) -> KiraConfig {
|
||||||
|
self.k_config.res_license = true;
|
||||||
|
self.k_config.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update(&mut self, message: Message) -> stage::StageAction {
|
pub fn update(&mut self, message: Message) -> stage::StageAction {
|
||||||
match message {
|
match message {
|
||||||
Message::Accept => stage::StageAction::Next(Self::accepted()),
|
Message::Accept => stage::StageAction::Next(self.accepted()),
|
||||||
Message::Back => stage::StageAction::Back,
|
Message::Back => stage::StageAction::Back,
|
||||||
Message::Decline => stage::StageAction::Abort,
|
Message::Decline => stage::StageAction::Abort,
|
||||||
}
|
}
|
||||||
|
|||||||
+29
-16
@@ -20,7 +20,7 @@
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
kira_theming,
|
kira_theming,
|
||||||
stage::{ConfigValue, KiraConfig, StageAction, StageResult},
|
stage::{KiraConfig, StageAction},
|
||||||
};
|
};
|
||||||
use iced::{Alignment, widget};
|
use iced::{Alignment, widget};
|
||||||
use rust_i18n::t;
|
use rust_i18n::t;
|
||||||
@@ -136,6 +136,13 @@ pub struct LocaleStage {
|
|||||||
locales: Vec<LocaleData>,
|
locales: Vec<LocaleData>,
|
||||||
lang_locale_text: String,
|
lang_locale_text: String,
|
||||||
all_locale_text: String,
|
all_locale_text: String,
|
||||||
|
k_config: KiraConfig,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
pub struct LocaleStageResult {
|
||||||
|
pub lang_locale: LocaleData,
|
||||||
|
pub formats_locale: LocaleData,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
@@ -147,11 +154,12 @@ pub enum Message {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl LocaleStage {
|
impl LocaleStage {
|
||||||
pub fn new(kira_config: &KiraConfig) -> Self {
|
pub fn new(k_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 = k_config
|
||||||
.get_stage("welcome")
|
.res_welcome
|
||||||
.and_then(|v| v.config.and_then(|v| v.get("loc_code").cloned()));
|
.as_ref()
|
||||||
|
.and_then(|rw| Some(rw.loc_code.clone()));
|
||||||
|
|
||||||
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 +182,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()
|
||||||
@@ -201,23 +211,26 @@ impl LocaleStage {
|
|||||||
locales: locales,
|
locales: locales,
|
||||||
lang_locale_text: lang_locale_text,
|
lang_locale_text: lang_locale_text,
|
||||||
all_locale_text: all_locale_text,
|
all_locale_text: all_locale_text,
|
||||||
|
k_config: k_config,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn gen_result(&self) -> StageResult {
|
fn gen_result(&mut self) -> KiraConfig {
|
||||||
if let Some(lang_locale) = &self.lang_locale
|
self.k_config.res_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")
|
Some(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")
|
Some(LocaleStageResult {
|
||||||
.add_val_string("lang_locale", loc.code.clone())
|
lang_locale: loc.clone(),
|
||||||
.add_val_string("formats_locale", loc.code)
|
formats_locale: loc,
|
||||||
}
|
})
|
||||||
|
};
|
||||||
|
self.k_config.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update(&mut self, message: Message) -> StageAction {
|
pub fn update(&mut self, message: Message) -> StageAction {
|
||||||
|
|||||||
@@ -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;
|
||||||
+22
-28
@@ -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 crate::stage;
|
use crate::stage::{self, KiraConfig};
|
||||||
use crate::stage::StageResult;
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
const REQIRE_NETWORK: bool = false;
|
||||||
#[derive(PartialEq, Eq)]
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub enum State {
|
pub enum State {
|
||||||
Cheking,
|
Cheking,
|
||||||
Normal,
|
Normal,
|
||||||
@@ -42,15 +41,21 @@ pub enum CheckResult {
|
|||||||
CheckError,
|
CheckError,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
pub struct NetworkStageResult {
|
||||||
|
pub internet_active: bool,
|
||||||
|
pub use_net_settings: bool,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct NetworkStage {
|
pub struct NetworkStage {
|
||||||
internet_active: bool,
|
internet_active: bool,
|
||||||
state: State,
|
state: State,
|
||||||
use_net_settings: bool,
|
use_net_settings: bool,
|
||||||
reqire_network: bool,
|
|
||||||
spinner_frames: apng::Frames,
|
spinner_frames: apng::Frames,
|
||||||
logo_handle: widget::image::Handle,
|
logo_handle: widget::image::Handle,
|
||||||
status_message: String,
|
status_message: String,
|
||||||
|
k_config: KiraConfig,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
@@ -95,21 +100,7 @@ async fn check_connected() -> CheckResult {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl NetworkStage {
|
impl NetworkStage {
|
||||||
pub fn new(toml_config: &Table) -> Self {
|
pub fn new(k_config: KiraConfig) -> Self {
|
||||||
let mut reqire_network = false;
|
|
||||||
|
|
||||||
if let Some(reqire_network_conf) = toml_config
|
|
||||||
.get("network")
|
|
||||||
.and_then(|v| v.as_table())
|
|
||||||
.and_then(|v| v.get("reqire_network"))
|
|
||||||
.and_then(|v| v.as_bool())
|
|
||||||
{
|
|
||||||
log::debug!("Config value reqire_network read {}", reqire_network_conf);
|
|
||||||
reqire_network = reqire_network_conf;
|
|
||||||
} else {
|
|
||||||
log::error!("Error parsing network config. Using default values.");
|
|
||||||
}
|
|
||||||
|
|
||||||
let spinner_frames =
|
let spinner_frames =
|
||||||
apng::Frames::from_bytes(crate::kira_theming::get_spiner_bytes()).unwrap();
|
apng::Frames::from_bytes(crate::kira_theming::get_spiner_bytes()).unwrap();
|
||||||
|
|
||||||
@@ -120,16 +111,19 @@ impl NetworkStage {
|
|||||||
internet_active: false,
|
internet_active: false,
|
||||||
state: State::Cheking,
|
state: State::Cheking,
|
||||||
use_net_settings: true,
|
use_net_settings: true,
|
||||||
reqire_network: reqire_network,
|
|
||||||
spinner_frames: spinner_frames,
|
spinner_frames: spinner_frames,
|
||||||
status_message: t!("network.checking").into(),
|
status_message: t!("network.checking").into(),
|
||||||
|
k_config: k_config,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn gen_result(&self) -> StageResult {
|
fn gen_result(&self) -> KiraConfig {
|
||||||
StageResult::new("network")
|
let mut k_config = self.k_config.clone();
|
||||||
.add_val_bool("internet_active", self.internet_active)
|
k_config.res_network = Some(NetworkStageResult {
|
||||||
.add_val_bool("use_net_settings", self.use_net_settings)
|
internet_active: self.internet_active,
|
||||||
|
use_net_settings: self.use_net_settings,
|
||||||
|
});
|
||||||
|
k_config
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update(&mut self, message: Message) -> UpdateResult {
|
pub fn update(&mut self, message: Message) -> UpdateResult {
|
||||||
@@ -170,7 +164,7 @@ impl NetworkStage {
|
|||||||
|
|
||||||
pub fn view(&self) -> iced::Element<'_, Message> {
|
pub fn view(&self) -> iced::Element<'_, Message> {
|
||||||
let next_button =
|
let next_button =
|
||||||
if (self.state == State::Cheking) || (!self.internet_active && self.reqire_network) {
|
if (self.state == State::Cheking) || (!self.internet_active && REQIRE_NETWORK) {
|
||||||
widget::button(widget::text(t!("button.next")))
|
widget::button(widget::text(t!("button.next")))
|
||||||
} else {
|
} else {
|
||||||
widget::button(widget::text(t!("button.next"))).on_press(Message::Next)
|
widget::button(widget::text(t!("button.next"))).on_press(Message::Next)
|
||||||
|
|||||||
+97
-42
@@ -24,11 +24,13 @@ use crate::kira_color_bar;
|
|||||||
use crate::kira_disk_layout::{BlkDev, FSType, PartInfo, PartLayout};
|
use crate::kira_disk_layout::{BlkDev, FSType, PartInfo, PartLayout};
|
||||||
use crate::kira_size::KiraSize;
|
use crate::kira_size::KiraSize;
|
||||||
use crate::kira_sysinfo;
|
use crate::kira_sysinfo;
|
||||||
use crate::stage::{StageAction, StageResult};
|
use crate::stage::{KiraConfig, StageAction};
|
||||||
use iced::{Alignment, Color, Element, Length, widget};
|
use iced::{Alignment, Color, Element, Length, widget};
|
||||||
use log;
|
use log;
|
||||||
use rust_i18n::t;
|
use rust_i18n::t;
|
||||||
use toml::Table;
|
|
||||||
|
const MIN_DISK_SIZE_MB: u64 = 8192;
|
||||||
|
const EFI_SIZE_MB: u64 = 128;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
pub enum SwapMode {
|
pub enum SwapMode {
|
||||||
@@ -68,6 +70,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,19 +79,36 @@ 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,
|
||||||
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,
|
||||||
|
k_config: KiraConfig,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
@@ -96,6 +117,7 @@ pub enum Message {
|
|||||||
SelectSwapMode(SwapMode),
|
SelectSwapMode(SwapMode),
|
||||||
ToggleZram(bool),
|
ToggleZram(bool),
|
||||||
ToggleSecureBoot(bool),
|
ToggleSecureBoot(bool),
|
||||||
|
ToggleEncrypDrive(bool),
|
||||||
SwapSizeFieldChange(String),
|
SwapSizeFieldChange(String),
|
||||||
SwapSizeIncrement,
|
SwapSizeIncrement,
|
||||||
SwapSizeDecrement,
|
SwapSizeDecrement,
|
||||||
@@ -104,18 +126,20 @@ pub enum Message {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl PartitionStage {
|
impl PartitionStage {
|
||||||
pub fn new(toml_config: &Table) -> Result<Self, String> {
|
pub fn new(k_config: KiraConfig) -> 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
|
|
||||||
.get("partition")
|
|
||||||
.and_then(|v| v.as_table())
|
|
||||||
.and_then(|v| v.get("min_disk_size_mb"))
|
|
||||||
.and_then(|v| v.as_integer())
|
|
||||||
.unwrap_or(8192);
|
|
||||||
|
|
||||||
let min_disk_size: KiraSize = KiraSize::new_mb(min_disk_size_mb.try_into().unwrap_or(8192));
|
let min_disk_size: KiraSize = KiraSize::new_mb(MIN_DISK_SIZE_MB);
|
||||||
|
let efi_size: KiraSize = KiraSize::new_mb(EFI_SIZE_MB);
|
||||||
|
|
||||||
|
let sec_boot_setup = match kira_sysinfo::get_secure_boot_status() {
|
||||||
|
Ok(sb_status) => sb_status.setup_mode,
|
||||||
|
Err(ex) => {
|
||||||
|
log::error!("Unable to get secure boot status! {}", ex);
|
||||||
|
false
|
||||||
|
}
|
||||||
|
};
|
||||||
match maybe_dev_list {
|
match maybe_dev_list {
|
||||||
Ok(dev_list) => {
|
Ok(dev_list) => {
|
||||||
let acepted_dev: Vec<BlkDev> = dev_list
|
let acepted_dev: Vec<BlkDev> = dev_list
|
||||||
@@ -143,12 +167,17 @@ 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,
|
||||||
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,
|
||||||
|
k_config: k_config,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
Err(ex) => Err(ex),
|
Err(ex) => Err(ex),
|
||||||
@@ -194,34 +223,42 @@ 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();
|
||||||
|
let patr_layout = PartLayout::gen_kira_layout(
|
||||||
|
dev.clone(),
|
||||||
|
self.efi_size,
|
||||||
|
swap_size,
|
||||||
|
None,
|
||||||
|
Some("lz4".into()),
|
||||||
|
);
|
||||||
self.generated_disk_parts = Some(
|
self.generated_disk_parts = Some(
|
||||||
PartLayout::gen_classic_layout(dev.clone(), KiraSize::new_mb(512), swap_size)
|
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(&mut self) -> KiraConfig {
|
||||||
StageResult::new("partition")
|
let disk_layout = self
|
||||||
.add_val_string(
|
.generated_part_layout
|
||||||
"device",
|
.clone()
|
||||||
self.device
|
.expect("Layout must be generated!");
|
||||||
.as_ref()
|
|
||||||
.and_then(|v| Some(v.name.clone()))
|
self.k_config.res_partition = Some(PartitionStageResult {
|
||||||
.unwrap_or_else(|| "NONE".to_string()),
|
swap_mode: self.swap_mode.clone().unwrap(),
|
||||||
)
|
swap_size: self.get_swap_size(),
|
||||||
.add_val_string(
|
efi_size: self.efi_size,
|
||||||
"swap_mode",
|
use_zram: self.use_zram,
|
||||||
self.swap_mode
|
secure_boot: self.secure_boot,
|
||||||
.clone()
|
encrypt_drive: true,
|
||||||
.unwrap_or(SwapMode::NoSwap)
|
password: Some("123456".to_string()),
|
||||||
.to_string(),
|
part_layout: disk_layout,
|
||||||
)
|
});
|
||||||
.add_val_bool("zram", self.use_zram)
|
|
||||||
.add_val_bool("secure_boot", self.secure_boot)
|
self.k_config.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update(&mut self, message: Message) -> StageAction {
|
pub fn update(&mut self, message: Message) -> StageAction {
|
||||||
@@ -241,20 +278,20 @@ 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();
|
||||||
@@ -264,6 +301,10 @@ impl PartitionStage {
|
|||||||
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();
|
||||||
@@ -320,7 +361,11 @@ impl PartitionStage {
|
|||||||
let dev_parts_content = widget::column![dev_parts_text, dev_parts_content];
|
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);
|
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);
|
let next_button = if self.device.is_some() {
|
||||||
|
widget::button(widget::text(t!("button.next"))).on_press(Message::Next)
|
||||||
|
} else {
|
||||||
|
widget::button(widget::text(t!("button.next")))
|
||||||
|
};
|
||||||
|
|
||||||
let selected_dev_text = match self.selected_disk_parts {
|
let selected_dev_text = match self.selected_disk_parts {
|
||||||
Some(_) => widget::text(t!("partition.current_dev_layout")),
|
Some(_) => widget::text(t!("partition.current_dev_layout")),
|
||||||
@@ -329,6 +374,14 @@ impl PartitionStage {
|
|||||||
let selected_dev_content = kira_color_bar::color_bar(self.selected_disk_parts.clone(), 64);
|
let selected_dev_content = kira_color_bar::color_bar(self.selected_disk_parts.clone(), 64);
|
||||||
let selected_dev_content = widget::column![selected_dev_text, selected_dev_content];
|
let selected_dev_content = widget::column![selected_dev_text, selected_dev_content];
|
||||||
|
|
||||||
|
let use_sec_boot_checkbox = if self.secure_boot_in_setup_mode {
|
||||||
|
widget::checkbox(self.secure_boot)
|
||||||
|
.label(t!("partition.use_secure_boot"))
|
||||||
|
.on_toggle(Message::ToggleSecureBoot)
|
||||||
|
} else {
|
||||||
|
widget::checkbox(self.secure_boot).label(t!("partition.use_secure_boot"))
|
||||||
|
};
|
||||||
|
|
||||||
widget::column![
|
widget::column![
|
||||||
widget::container(
|
widget::container(
|
||||||
widget::column![
|
widget::column![
|
||||||
@@ -354,9 +407,11 @@ impl PartitionStage {
|
|||||||
.label(t!("partition.use_zram"))
|
.label(t!("partition.use_zram"))
|
||||||
.on_toggle(Message::ToggleZram),
|
.on_toggle(Message::ToggleZram),
|
||||||
widget::rule::horizontal(2),
|
widget::rule::horizontal(2),
|
||||||
widget::checkbox(self.secure_boot)
|
use_sec_boot_checkbox,
|
||||||
.label(t!("partition.use_secure_boot"))
|
widget::rule::horizontal(2),
|
||||||
.on_toggle(Message::ToggleSecureBoot),
|
widget::checkbox(self.encrypt_drive)
|
||||||
|
.label(t!("partition.encrypt_drive"))
|
||||||
|
.on_toggle(Message::ToggleEncrypDrive),
|
||||||
widget::rule::horizontal(2),
|
widget::rule::horizontal(2),
|
||||||
selected_dev_content,
|
selected_dev_content,
|
||||||
dev_parts_content,
|
dev_parts_content,
|
||||||
|
|||||||
@@ -0,0 +1,301 @@
|
|||||||
|
// <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::{KiraConfig, StageAction};
|
||||||
|
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,
|
||||||
|
k_config: KiraConfig,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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(k_config: KiraConfig) -> 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,
|
||||||
|
k_config: k_config,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn gen_result(&mut self) -> KiraConfig {
|
||||||
|
self.k_config.res_security = Some(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(),
|
||||||
|
});
|
||||||
|
self.k_config.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()
|
||||||
|
}
|
||||||
|
}
|
||||||
+26
-15
@@ -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::{KiraConfig, StageAction};
|
||||||
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;
|
||||||
|
|
||||||
@@ -58,6 +58,12 @@ pub struct TimeZoneStage {
|
|||||||
zone_id: Option<usize>,
|
zone_id: Option<usize>,
|
||||||
selected_zone: Option<TimeZoneData>,
|
selected_zone: Option<TimeZoneData>,
|
||||||
selected_zone_text: String,
|
selected_zone_text: String,
|
||||||
|
k_config: KiraConfig,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct TimeZoneStageResult {
|
||||||
|
pub time_zone: TimeZoneData,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
@@ -100,7 +106,7 @@ fn get_timezones_blocking() -> Result<Vec<TimeZoneData>, String> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl TimeZoneStage {
|
impl TimeZoneStage {
|
||||||
pub fn new() -> Self {
|
pub fn new(k_config: KiraConfig) -> Self {
|
||||||
match get_timezones_blocking() {
|
match get_timezones_blocking() {
|
||||||
Ok(time_zones_data_list) => {
|
Ok(time_zones_data_list) => {
|
||||||
let mut time_zones_map: HashMap<String, Vec<String>> = HashMap::new();
|
let mut time_zones_map: HashMap<String, Vec<String>> = HashMap::new();
|
||||||
@@ -125,6 +131,7 @@ impl TimeZoneStage {
|
|||||||
zone_id: None,
|
zone_id: None,
|
||||||
selected_zone: None,
|
selected_zone: None,
|
||||||
selected_zone_text: String::new(),
|
selected_zone_text: String::new(),
|
||||||
|
k_config: k_config,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(ex) => {
|
Err(ex) => {
|
||||||
@@ -140,20 +147,20 @@ impl TimeZoneStage {
|
|||||||
zone_id: None,
|
zone_id: None,
|
||||||
selected_zone: None,
|
selected_zone: None,
|
||||||
selected_zone_text: String::new(),
|
selected_zone_text: String::new(),
|
||||||
|
k_config: k_config,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn gen_result(&self) -> StageResult {
|
fn gen_result(&mut self) -> KiraConfig {
|
||||||
if let Some(time_zone) = &self.selected_zone {
|
self.k_config.res_timezone = Some(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")
|
self.k_config.clone()
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update(&mut self, message: Message) -> StageAction {
|
pub fn update(&mut self, message: Message) -> StageAction {
|
||||||
@@ -201,10 +208,14 @@ 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(
|
||||||
.map(Message::SelectZone),
|
&self.zones,
|
||||||
|
self.zone_id,
|
||||||
|
Length::FillPortion(1)
|
||||||
|
)
|
||||||
|
.map(Message::SelectZone),
|
||||||
]
|
]
|
||||||
.padding([0,10])
|
.padding([0, 10])
|
||||||
.spacing(10)
|
.spacing(10)
|
||||||
.height(Length::Shrink),
|
.height(Length::Shrink),
|
||||||
widget::text(self.selected_zone_text.clone())
|
widget::text(self.selected_zone_text.clone())
|
||||||
|
|||||||
+40
-30
@@ -1,26 +1,27 @@
|
|||||||
// <Kira Installer - universal Linux installer.>
|
// <Kira Installer - universal Linux installer.>
|
||||||
// Copyright (C) <2026> <Kira Foundation>
|
// Copyright (C) <2026> <Kira Foundation>
|
||||||
|
|
||||||
// This program is free software: you can redistribute it and/or modify
|
// 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
|
// it under the terms of the GNU General Public License as published by
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
// (at your option) any later version.
|
// (at your option) any later version.
|
||||||
|
|
||||||
// This program is distributed in the hope that it will be useful,
|
// This program is distributed in the hope that it will be useful,
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
// GNU General Public License for more details.
|
// 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/>.
|
|
||||||
|
|
||||||
|
// 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 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::{KiraConfig, StageAction},
|
||||||
|
};
|
||||||
use iced::{Alignment, widget};
|
use iced::{Alignment, widget};
|
||||||
use rust_i18n::t;
|
use rust_i18n::t;
|
||||||
|
|
||||||
@@ -36,10 +37,17 @@ impl std::fmt::Display for LocData {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
pub struct WelcomeStageResult {
|
||||||
|
pub loc_code: String,
|
||||||
|
pub loc_name: String,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct WelcomeStage {
|
pub struct WelcomeStage {
|
||||||
locale: Option<LocData>,
|
locale: Option<LocData>,
|
||||||
locales: Vec<LocData>,
|
locales: Vec<LocData>,
|
||||||
|
k_config: KiraConfig,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
@@ -50,8 +58,7 @@ pub enum Message {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl WelcomeStage {
|
impl WelcomeStage {
|
||||||
pub fn new() -> Self {
|
pub fn new(k_config: KiraConfig) -> Self {
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
locale: Some(LocData {
|
locale: Some(LocData {
|
||||||
name: "English".into(),
|
name: "English".into(),
|
||||||
@@ -64,19 +71,23 @@ impl WelcomeStage {
|
|||||||
code: s.to_string(),
|
code: s.to_string(),
|
||||||
})
|
})
|
||||||
.collect(),
|
.collect(),
|
||||||
|
k_config: k_config,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn gen_result(&self) -> StageResult {
|
fn gen_result(&mut self) -> KiraConfig {
|
||||||
if let Some(loc) = &self.locale {
|
self.k_config.res_welcome = Some(if let Some(loc) = &self.locale {
|
||||||
StageResult::new("welcome")
|
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")
|
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(),
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
self.k_config.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update(&mut self, message: Message) -> StageAction {
|
pub fn update(&mut self, message: Message) -> StageAction {
|
||||||
@@ -96,8 +107,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(
|
||||||
|
|||||||
Reference in New Issue
Block a user