From 23905877c7170f9a9123e204b79e43b44501d316 Mon Sep 17 00:00:00 2001 From: Lucy Date: Tue, 30 Sep 2025 19:51:26 +0200 Subject: [PATCH] working --- src/main.rs | 105 ++++++++++++++++++++++++++------------------ src/mirrors.rs | 2 +- src/tui.rs | 115 +++++++++++++++++++++++++++++++++++++++++-------- 3 files changed, 162 insertions(+), 60 deletions(-) diff --git a/src/main.rs b/src/main.rs index d93c2af..c18abaf 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,6 +4,9 @@ mod mirrors; mod version_check; mod wget_list; +#[cfg(feature = "tui")] +mod tui; // Importiere das TUI-Modul, wenn das Feature aktiv ist + use console::style; use rand::Rng; use std::collections::HashMap; @@ -11,53 +14,73 @@ use std::env; use std::path::PathBuf; fn main() -> Result<(), Box> { - // --- Run host system version checks --- - if version_check::run_version_checks() { - eprintln!( - "{} Host system does not meet minimum requirements. Exiting.", - style("❌").red().bold() - ); - std::process::exit(1); + #[cfg(feature = "tui")] + { + // Wenn das TUI-Feature aktiv ist, starte das TUI-Menü + tui::tui_menu()?; + Ok(()) } - println!( - "{} All version checks passed. Starting downloader...", - style("✅").green().bold() - ); - - // --- Determine LFS sources path --- - let lfs_sources = match env::var("LFS") { - Ok(lfs) => PathBuf::from(lfs).join("sources"), - Err(_) => { - let mut rng = rand::rng(); - let random_number: u32 = rng.random_range(1000..=9999); - let tmp_path = format!("/tmp/lfs_{}", random_number); - println!( - "{} Using temporary path {}", - style("ℹ️").blue(), - style(&tmp_path).yellow() + #[cfg(not(feature = "tui"))] + { + // Wenn das TUI-Feature NICHT aktiv ist, führe die CLI-Logik aus + // --- Run host system version checks --- + if version_check::run_version_checks() { + eprintln!( + "{} Host system does not meet minimum requirements. Exiting.", + style("❌").red().bold() ); - PathBuf::from(tmp_path).join("sources") + std::process::exit(1); } - }; - // --- Choose mirror and fetch wget list --- - let package_mirror = mirrors::choose_package_mirror(); - let wget_list = wget_list::get_wget_list()?; + println!( + "{} All version checks passed. Starting downloader...", + style("✅").green().bold() + ); - // --- Prepare MD5 map --- - let mut md5_map: HashMap = HashMap::new(); - let md5_content = md5_utils::get_md5sums()?; - for line in md5_content.lines() { - let mut parts = line.split_whitespace(); - if let (Some(hash), Some(filename)) = (parts.next(), parts.next()) { - md5_map.insert(filename.to_string(), hash.to_string()); + // --- Determine LFS sources path --- + let lfs_sources = match env::var("LFS") { + Ok(lfs) => PathBuf::from(lfs).join("sources"), + Err(_) => { + let mut rng = rand::thread_rng(); // Verwende thread_rng() statt rng() + let random_number: u32 = rng.gen_range(1000..=9999); // Verwende gen_range() statt random_range() + let tmp_path = format!("/tmp/lfs_{}", random_number); + println!( + "{} Using temporary path {}", + style("ℹ️").blue(), + style(&tmp_path).yellow() + ); + PathBuf::from(tmp_path).join("sources") + } + }; + + // --- Choose mirror and fetch wget list --- + // Diese Zeile wird entfernt, da die Mirror-Auswahl in der TUI erfolgt + // let package_mirror = mirrors::choose_package_mirror(); + + // Da die Mirror-Auswahl nun in der TUI erfolgt, müssen wir hier einen Standardwert oder eine andere Logik verwenden, + // wenn das TUI nicht aktiv ist. Für dieses Beispiel nehmen wir an, dass wir keinen Mirror verwenden, + // wenn das TUI nicht aktiv ist, oder wir könnten eine andere CLI-basierte Auswahl implementieren. + // Für den Moment setzen wir es auf None, was bedeutet, dass der Standard-Mirror verwendet wird. + let package_mirror: Option = None; + + + let wget_list = wget_list::get_wget_list()?; + + // --- Prepare MD5 map --- + let mut md5_map: HashMap = HashMap::new(); + let md5_content = md5_utils::get_md5sums()?; + for line in md5_content.lines() { + let mut parts = line.split_whitespace(); + if let (Some(hash), Some(filename)) = (parts.next(), parts.next()) { + md5_map.insert(filename.to_string(), hash.to_string()); + } } + + // --- Download files --- + downloader::download_files(&wget_list, &lfs_sources, package_mirror, Some(&md5_map))?; + + println!("{} All done!", style("🎉").green().bold()); + Ok(()) } - - // --- Download files --- - downloader::download_files(&wget_list, &lfs_sources, package_mirror, Some(&md5_map))?; - - println!("{} All done!", style("🎉").green().bold()); - Ok(()) } diff --git a/src/mirrors.rs b/src/mirrors.rs index b488108..72f2bc6 100644 --- a/src/mirrors.rs +++ b/src/mirrors.rs @@ -43,7 +43,7 @@ pub fn choose_package_mirror() -> Option { } } -fn fetch_mirrors() -> Result, Box> { +pub fn fetch_mirrors() -> Result, Box> { let client = Client::new(); let res = client .get("https://www.linuxfromscratch.org/lfs/mirrors.html#files") diff --git a/src/tui.rs b/src/tui.rs index 6e152a5..433a78a 100644 --- a/src/tui.rs +++ b/src/tui.rs @@ -12,23 +12,31 @@ use ratatui::{ backend::CrosstermBackend, layout::{Constraint, Direction, Layout}, style::{Color, Style}, - widgets::{Block, Borders, List, ListItem, ListState}, + widgets::{Block, Borders, List, ListItem, ListState, Paragraph}, }; #[cfg(feature = "tui")] use std::{ collections::HashMap, - io::{self, stdout}, + io::stdout, path::PathBuf, sync::{Arc, Mutex}, thread, - time::Duration, + time::{Duration, Instant}, + fs, // Added for file system operations }; +#[cfg(feature = "tui")] // Hinzugefügt: Import des Rng-Traits +use rand::Rng; #[cfg(feature = "tui")] -fn init_environment() -> PathBuf { - let tmp_path = format!("/tmp/lfs_{}", rand::random::() % 9000 + 1000); - println!("ℹ️ Using temporary path {}", tmp_path); - PathBuf::from(tmp_path).join("sources") +fn init_environment() -> Result> { + let mut rng = rand::rng(); + let random_number: u32 = rng.gen_range(1000..=9999); + let tmp_base_path = PathBuf::from(format!("/tmp/lfs_{}", random_number)); + let lfs_sources_path = tmp_base_path.join("sources"); + + std::fs::create_dir_all(&lfs_sources_path)?; + + Ok(lfs_sources_path) } #[cfg(feature = "tui")] @@ -50,7 +58,7 @@ fn select_mirrors_tui(mirrors: Vec) -> Vec { loop { terminal .draw(|f| { - let size = f.size(); + let size = f.area(); let items: Vec = mirrors .iter() .enumerate() @@ -113,13 +121,12 @@ fn download_packages_tui(lfs_sources: &PathBuf) { let mirrors_list = mirrors::fetch_mirrors().unwrap_or_default(); let selected_mirrors = select_mirrors_tui(mirrors_list); if selected_mirrors.is_empty() { - println!("⚠️ No mirrors selected!"); return; } - let wget_list = wget_list::get_wget_list().unwrap_or_default(); + let wget_list_content = wget_list::get_wget_list().unwrap_or_default(); + let wget_list: Vec = wget_list_content.lines().map(|s| s.to_string()).collect(); if wget_list.is_empty() { - println!("⚠️ No packages to download!"); return; } @@ -175,7 +182,7 @@ fn download_packages_tui(lfs_sources: &PathBuf) { loop { terminal .draw(|f| { - let size = f.size(); + let size = f.area(); let items: Vec = { let state = download_state.lock().unwrap(); state @@ -201,7 +208,6 @@ fn download_packages_tui(lfs_sources: &PathBuf) { disable_raw_mode().unwrap(); execute!(terminal.backend_mut(), LeaveAlternateScreen).unwrap(); - println!("🎉 All downloads finished!"); } #[cfg(feature = "tui")] @@ -216,16 +222,19 @@ pub fn tui_menu() -> Result<(), Box> { "🌱 Init environment", "📦 Download packages", "🔍 Check status", + "🧹 Clean up temp directories", // Neuer Menüpunkt "❌ Exit", ]; let mut state = ListState::default(); state.select(Some(0)); let mut lfs_sources: Option = None; + let mut status_message: Option = None; // Für Statusmeldungen + let mut status_message_timer: Option = None; // Für temporäre Meldungen loop { terminal.draw(|f| { - let size = f.size(); + let size = f.area(); let block = Block::default() .title("✨ lpkg TUI 🌈") .borders(Borders::ALL); @@ -246,8 +255,35 @@ pub fn tui_menu() -> Result<(), Box> { let list = List::new(vec![list_item]).block(Block::default().borders(Borders::ALL)); f.render_widget(list, chunks[i]); } + + // Statusmeldung anzeigen + if let Some(msg) = &status_message { + let msg_block = Block::default() + .borders(Borders::NONE) + .style(Style::default().fg(Color::Yellow)); + let paragraph = Paragraph::new(msg.as_str()).block(msg_block); + // Positionierung der Statusmeldung (z.B. unten in der Mitte) + let msg_area = Layout::default() + .direction(Direction::Horizontal) + .constraints([Constraint::Percentage(10), Constraint::Percentage(80), Constraint::Percentage(10)]) + .split(size)[1]; // Mittlerer Bereich + let msg_area = Layout::default() + .direction(Direction::Vertical) + .constraints([Constraint::Min(0), Constraint::Length(1)]) + .split(msg_area)[1]; // Unterste Zeile + f.render_widget(paragraph, msg_area); + } })?; + // Statusmeldung nach einer bestimmten Zeit ausblenden + if let Some(timer) = status_message_timer { + if timer.elapsed() > Duration::from_secs(3) { // 3 Sekunden anzeigen + status_message = None; + status_message_timer = None; + } + } + + if let Event::Key(key) = event::read()? { match key.code { KeyCode::Down => { @@ -264,19 +300,44 @@ pub fn tui_menu() -> Result<(), Box> { } KeyCode::Enter => match state.selected() { Some(0) => { - lfs_sources = Some(init_environment()); + match init_environment() { + Ok(path) => { + lfs_sources = Some(path.clone()); + status_message = Some(format!("✅ Environment initialized: {}", path.display())); + } + Err(e) => { + status_message = Some(format!("❌ Failed to initialize environment: {}", e)); + } + } + status_message_timer = Some(std::time::Instant::now()); } Some(1) => { if let Some(ref path) = lfs_sources { download_packages_tui(path); } else { - println!("⚠️ Please initialize environment first!"); + status_message = Some("⚠️ Please initialize environment first!".to_string()); + status_message_timer = Some(std::time::Instant::now()); } } Some(2) => { - println!("🔍 Status selected! (TODO)"); + status_message = Some("🔍 Status selected! (TODO)".to_string()); + status_message_timer = Some(std::time::Instant::now()); } - Some(3) | _ => break, + Some(3) => { + // Aufruf der neuen Bereinigungsfunktion + status_message = Some("🧹 Cleaning up temporary directories...".to_string()); + status_message_timer = Some(std::time::Instant::now()); + match cleanup_temp_directories() { + Ok(count) => { + status_message = Some(format!("✅ Cleaned up {} temporary directories.", count)); + } + Err(e) => { + status_message = Some(format!("❌ Failed to clean up: {}", e)); + } + } + status_message_timer = Some(std::time::Instant::now()); + } + Some(4) | _ => break, }, KeyCode::Esc => break, _ => {} @@ -289,3 +350,21 @@ pub fn tui_menu() -> Result<(), Box> { terminal.show_cursor()?; Ok(()) } + +#[cfg(feature = "tui")] +fn cleanup_temp_directories() -> Result> { + let mut cleaned_count = 0; + for entry in std::fs::read_dir("/tmp")? { + let entry = entry?; + let path = entry.path(); + if path.is_dir() { + if let Some(dir_name) = path.file_name().and_then(|s| s.to_str()) { + if dir_name.starts_with("lfs_") { + std::fs::remove_dir_all(&path)?; + cleaned_count += 1; + } + } + } + } + Ok(cleaned_count) +}