use yansi::{Color, Paint}; use crate::config::InstallConfig; pub fn print_config(conf: &InstallConfig) { println!("🚀 Install Configuration:"); let mut root_info = Tree::new(); let mut drive_info = Tree::new(); drive_info.add_str(format!( "💾 {} {}", conf.drive.boot.paint(Color::Red), "[EFI]".paint(Color::Blue) )); drive_info.add_str(format!( "{} {} {}", if conf.drive.encryption.is_some() { "🔒" } else { "💾" }, conf.drive.root.paint(Color::Red), "[ROOT]".paint(Color::Blue) )); root_info.add_tree("💾 Drive Selection", drive_info); let mut general_info = Tree::new(); general_info.add_str(format!( "💎 {} {}", "Mode:".paint(Color::Yellow), conf.general.mode )); general_info.add_str(format!( "🖥️ {} {}", "Hostname:".paint(Color::Yellow), conf.general.hostname )); general_info.add_str(format!( "⌨️ {} {} {}", "Keyboard:".paint(Color::Yellow), conf.general.keyboard_layout, conf.general .keyboard_variant .as_ref() .unwrap_or(&String::new()) )); general_info.add_str(format!( "🌍 {} {}", "Locale:".paint(Color::Yellow), conf.general.locale )); general_info.add_str(format!( "⌛ {} {}", "Timezone:".paint(Color::Yellow), conf.general.timezone )); if conf.general.root_password.is_some() { general_info.add_str(format!( "🔑 {} {}", "Root Password".paint(Color::Yellow), "✔️".paint(Color::Green) )); } root_info.add_tree("🔨 General", general_info); let mut pkg_info = Tree::new(); if conf.pkg.docker.unwrap_or_default() { pkg_info.add_str(format!("🐳 Docker {}", "✔️".paint(Color::Green))); } if conf.pkg.virtualization.unwrap_or_default() { pkg_info.add_str(format!("🎃 Virtualization {}", "✔️".paint(Color::Green))); } if !conf.pkg.pkg.is_empty() { pkg_info.add_str(format!( "📦 Additional packages: {}", conf.pkg.pkg.join(" ") )); } root_info.add_tree("📦 Packages", pkg_info); let mut users_info = Tree::new(); let empty = Vec::new(); let user_conf = conf.user.as_ref().unwrap_or(&empty); for user in user_conf { let mut groups = Vec::new(); if user.doas_root.unwrap_or_default() { groups.push("🔑"); } if user.docker.unwrap_or_default() { groups.push("🐋"); } users_info.add_str(format!( "👤 {}{}", user.name, if !groups.is_empty() { format!(" [ {} ]", groups.join(" ")) } else { String::new() } )); } root_info.add_tree("🧛 Users", users_info); if let Some(ssh_conf) = &conf.ssh { let mut ssh_info = Tree::new(); if let Some(sshd_conf) = &ssh_conf.sshd_config { ssh_info.add_str(format!("📖 Installing {sshd_conf} as sshd_config")); } if let Some(keys) = &ssh_conf.key { for key in keys { let key_origin = key.key.split(" ").nth(2).unwrap_or_default(); ssh_info.add_str(format!( "🔑 Key {} for {}", key_origin.paint(Color::Blue), key.users.join(", ").paint(Color::Yellow) )); } } root_info.add_tree("🌐 SSH", ssh_info); } if let Some(ai_conf) = &conf.ai { let mut ai_info = Tree::new(); if ai_conf.gpu { ai_info.add_str(format!("🟩 Use CUDA {}", "✔️".paint(Color::Green))); } if let Some(models) = &ai_conf.models { ai_info.add_str(format!("⬇️ Pull Models: {}", models.join(", "))); } root_info.add_tree("🦙 Ollama", ai_info); } println!("{}", root_info.render(0)); } pub struct Tree { elements: Vec, } pub enum TreeEntry { Tree(String, Tree), Str(String), } impl Tree { pub fn new() -> Self { Self { elements: vec![] } } pub fn add_tree(&mut self, key: &str, item: Tree) { self.elements.push(TreeEntry::Tree(key.to_string(), item)); } pub fn add_str(&mut self, item: String) { self.elements.push(TreeEntry::Str(item)); } pub fn render(&self, depth: i32) -> String { const DEPTH_INCREASE: i32 = 3; let mut ret = String::new(); let len = self.elements.len(); for (index, val) in self.elements.iter().enumerate() { let padding = pad_with_bars(depth, DEPTH_INCREASE); let path_repr = if (index + 1) == len { if matches!(val, TreeEntry::Tree(_, _)) { "├─" } else { "└─" } } else { "├─" }; match val { TreeEntry::Tree(key, tree) => { if !tree.elements.is_empty() { ret.push_str(&format!("{padding}{path_repr} {key}\n")); ret.push_str(&tree.render(depth + DEPTH_INCREASE)) } } TreeEntry::Str(str) => { ret.push_str(&format!("{padding}{path_repr} {str}\n")); } } } ret } } pub fn pad_with_bars(n: i32, depth_inc: i32) -> String { let amount = n / depth_inc; let mut ret = String::new(); if amount >= 1 { ret.push_str("│"); } ret.push_str(&pad(n - amount)); ret } pub fn pad(n: i32) -> String { let mut ret = String::new(); for _ in 0..n { ret.push_str(" "); } ret }