working
This commit is contained in:
parent
bc3560f1f8
commit
23905877c7
3 changed files with 162 additions and 60 deletions
29
src/main.rs
29
src/main.rs
|
|
@ -4,6 +4,9 @@ mod mirrors;
|
||||||
mod version_check;
|
mod version_check;
|
||||||
mod wget_list;
|
mod wget_list;
|
||||||
|
|
||||||
|
#[cfg(feature = "tui")]
|
||||||
|
mod tui; // Importiere das TUI-Modul, wenn das Feature aktiv ist
|
||||||
|
|
||||||
use console::style;
|
use console::style;
|
||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
@ -11,6 +14,16 @@ use std::env;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
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 ---
|
// --- Run host system version checks ---
|
||||||
if version_check::run_version_checks() {
|
if version_check::run_version_checks() {
|
||||||
eprintln!(
|
eprintln!(
|
||||||
|
|
@ -29,8 +42,8 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
let lfs_sources = match env::var("LFS") {
|
let lfs_sources = match env::var("LFS") {
|
||||||
Ok(lfs) => PathBuf::from(lfs).join("sources"),
|
Ok(lfs) => PathBuf::from(lfs).join("sources"),
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
let mut rng = rand::rng();
|
let mut rng = rand::thread_rng(); // Verwende thread_rng() statt rng()
|
||||||
let random_number: u32 = rng.random_range(1000..=9999);
|
let random_number: u32 = rng.gen_range(1000..=9999); // Verwende gen_range() statt random_range()
|
||||||
let tmp_path = format!("/tmp/lfs_{}", random_number);
|
let tmp_path = format!("/tmp/lfs_{}", random_number);
|
||||||
println!(
|
println!(
|
||||||
"{} Using temporary path {}",
|
"{} Using temporary path {}",
|
||||||
|
|
@ -42,7 +55,16 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
};
|
};
|
||||||
|
|
||||||
// --- Choose mirror and fetch wget list ---
|
// --- 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()?;
|
let wget_list = wget_list::get_wget_list()?;
|
||||||
|
|
||||||
// --- Prepare MD5 map ---
|
// --- Prepare MD5 map ---
|
||||||
|
|
@ -60,4 +82,5 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
|
||||||
println!("{} All done!", style("🎉").green().bold());
|
println!("{} All done!", style("🎉").green().bold());
|
||||||
Ok(())
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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 client = Client::new();
|
||||||
let res = client
|
let res = client
|
||||||
.get("https://www.linuxfromscratch.org/lfs/mirrors.html#files")
|
.get("https://www.linuxfromscratch.org/lfs/mirrors.html#files")
|
||||||
|
|
|
||||||
115
src/tui.rs
115
src/tui.rs
|
|
@ -12,23 +12,31 @@ use ratatui::{
|
||||||
backend::CrosstermBackend,
|
backend::CrosstermBackend,
|
||||||
layout::{Constraint, Direction, Layout},
|
layout::{Constraint, Direction, Layout},
|
||||||
style::{Color, Style},
|
style::{Color, Style},
|
||||||
widgets::{Block, Borders, List, ListItem, ListState},
|
widgets::{Block, Borders, List, ListItem, ListState, Paragraph},
|
||||||
};
|
};
|
||||||
#[cfg(feature = "tui")]
|
#[cfg(feature = "tui")]
|
||||||
use std::{
|
use std::{
|
||||||
collections::HashMap,
|
collections::HashMap,
|
||||||
io::{self, stdout},
|
io::stdout,
|
||||||
path::PathBuf,
|
path::PathBuf,
|
||||||
sync::{Arc, Mutex},
|
sync::{Arc, Mutex},
|
||||||
thread,
|
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")]
|
#[cfg(feature = "tui")]
|
||||||
fn init_environment() -> PathBuf {
|
fn init_environment() -> Result<PathBuf, Box<dyn std::error::Error>> {
|
||||||
let tmp_path = format!("/tmp/lfs_{}", rand::random::<u32>() % 9000 + 1000);
|
let mut rng = rand::rng();
|
||||||
println!("ℹ️ Using temporary path {}", tmp_path);
|
let random_number: u32 = rng.gen_range(1000..=9999);
|
||||||
PathBuf::from(tmp_path).join("sources")
|
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")]
|
#[cfg(feature = "tui")]
|
||||||
|
|
@ -50,7 +58,7 @@ fn select_mirrors_tui(mirrors: Vec<String>) -> Vec<String> {
|
||||||
loop {
|
loop {
|
||||||
terminal
|
terminal
|
||||||
.draw(|f| {
|
.draw(|f| {
|
||||||
let size = f.size();
|
let size = f.area();
|
||||||
let items: Vec<ListItem> = mirrors
|
let items: Vec<ListItem> = mirrors
|
||||||
.iter()
|
.iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
|
|
@ -113,13 +121,12 @@ fn download_packages_tui(lfs_sources: &PathBuf) {
|
||||||
let mirrors_list = mirrors::fetch_mirrors().unwrap_or_default();
|
let mirrors_list = mirrors::fetch_mirrors().unwrap_or_default();
|
||||||
let selected_mirrors = select_mirrors_tui(mirrors_list);
|
let selected_mirrors = select_mirrors_tui(mirrors_list);
|
||||||
if selected_mirrors.is_empty() {
|
if selected_mirrors.is_empty() {
|
||||||
println!("⚠️ No mirrors selected!");
|
|
||||||
return;
|
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() {
|
if wget_list.is_empty() {
|
||||||
println!("⚠️ No packages to download!");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -175,7 +182,7 @@ fn download_packages_tui(lfs_sources: &PathBuf) {
|
||||||
loop {
|
loop {
|
||||||
terminal
|
terminal
|
||||||
.draw(|f| {
|
.draw(|f| {
|
||||||
let size = f.size();
|
let size = f.area();
|
||||||
let items: Vec<ListItem> = {
|
let items: Vec<ListItem> = {
|
||||||
let state = download_state.lock().unwrap();
|
let state = download_state.lock().unwrap();
|
||||||
state
|
state
|
||||||
|
|
@ -201,7 +208,6 @@ fn download_packages_tui(lfs_sources: &PathBuf) {
|
||||||
|
|
||||||
disable_raw_mode().unwrap();
|
disable_raw_mode().unwrap();
|
||||||
execute!(terminal.backend_mut(), LeaveAlternateScreen).unwrap();
|
execute!(terminal.backend_mut(), LeaveAlternateScreen).unwrap();
|
||||||
println!("🎉 All downloads finished!");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "tui")]
|
#[cfg(feature = "tui")]
|
||||||
|
|
@ -216,16 +222,19 @@ pub fn tui_menu() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
"🌱 Init environment",
|
"🌱 Init environment",
|
||||||
"📦 Download packages",
|
"📦 Download packages",
|
||||||
"🔍 Check status",
|
"🔍 Check status",
|
||||||
|
"🧹 Clean up temp directories", // Neuer Menüpunkt
|
||||||
"❌ Exit",
|
"❌ Exit",
|
||||||
];
|
];
|
||||||
let mut state = ListState::default();
|
let mut state = ListState::default();
|
||||||
state.select(Some(0));
|
state.select(Some(0));
|
||||||
|
|
||||||
let mut lfs_sources: Option<PathBuf> = None;
|
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 {
|
loop {
|
||||||
terminal.draw(|f| {
|
terminal.draw(|f| {
|
||||||
let size = f.size();
|
let size = f.area();
|
||||||
let block = Block::default()
|
let block = Block::default()
|
||||||
.title("✨ lpkg TUI 🌈")
|
.title("✨ lpkg TUI 🌈")
|
||||||
.borders(Borders::ALL);
|
.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));
|
let list = List::new(vec![list_item]).block(Block::default().borders(Borders::ALL));
|
||||||
f.render_widget(list, chunks[i]);
|
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()? {
|
if let Event::Key(key) = event::read()? {
|
||||||
match key.code {
|
match key.code {
|
||||||
KeyCode::Down => {
|
KeyCode::Down => {
|
||||||
|
|
@ -264,19 +300,44 @@ pub fn tui_menu() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
}
|
}
|
||||||
KeyCode::Enter => match state.selected() {
|
KeyCode::Enter => match state.selected() {
|
||||||
Some(0) => {
|
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) => {
|
Some(1) => {
|
||||||
if let Some(ref path) = lfs_sources {
|
if let Some(ref path) = lfs_sources {
|
||||||
download_packages_tui(path);
|
download_packages_tui(path);
|
||||||
} else {
|
} 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) => {
|
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,
|
KeyCode::Esc => break,
|
||||||
_ => {}
|
_ => {}
|
||||||
|
|
@ -289,3 +350,21 @@ pub fn tui_menu() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
terminal.show_cursor()?;
|
terminal.show_cursor()?;
|
||||||
Ok(())
|
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)
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue