This commit is contained in:
Lucy 2025-09-30 19:51:26 +02:00
parent bc3560f1f8
commit 23905877c7
3 changed files with 162 additions and 60 deletions

View file

@ -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,6 +14,16 @@ use std::env;
use std::path::PathBuf;
fn main() -> Result<(), Box<dyn std::error::Error>> {
#[cfg(feature = "tui")]
{
// Wenn das TUI-Feature aktiv ist, starte das TUI-Menü
tui::tui_menu()?;
Ok(())
}
#[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!(
@ -29,8 +42,8 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
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 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 {}",
@ -42,7 +55,16 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
};
// --- Choose mirror and fetch wget list ---
let package_mirror = mirrors::choose_package_mirror();
// 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<String> = None;
let wget_list = wget_list::get_wget_list()?;
// --- Prepare MD5 map ---
@ -61,3 +83,4 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
println!("{} All done!", style("🎉").green().bold());
Ok(())
}
}

View file

@ -43,7 +43,7 @@ pub fn choose_package_mirror() -> Option<String> {
}
}
fn fetch_mirrors() -> Result<Vec<String>, Box<dyn std::error::Error>> {
pub fn fetch_mirrors() -> Result<Vec<String>, Box<dyn std::error::Error>> {
let client = Client::new();
let res = client
.get("https://www.linuxfromscratch.org/lfs/mirrors.html#files")

View file

@ -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::<u32>() % 9000 + 1000);
println!(" Using temporary path {}", tmp_path);
PathBuf::from(tmp_path).join("sources")
fn init_environment() -> Result<PathBuf, Box<dyn std::error::Error>> {
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<String>) -> Vec<String> {
loop {
terminal
.draw(|f| {
let size = f.size();
let size = f.area();
let items: Vec<ListItem> = 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<String> = 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<ListItem> = {
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<dyn std::error::Error>> {
"🌱 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<PathBuf> = None;
let mut status_message: Option<String> = None; // Für Statusmeldungen
let mut status_message_timer: Option<std::time::Instant> = 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<dyn std::error::Error>> {
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<dyn std::error::Error>> {
}
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<dyn std::error::Error>> {
terminal.show_cursor()?;
Ok(())
}
#[cfg(feature = "tui")]
fn cleanup_temp_directories() -> Result<usize, Box<dyn std::error::Error>> {
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)
}