working
This commit is contained in:
parent
23905877c7
commit
208234df27
7 changed files with 478 additions and 127 deletions
33
src/html.rs
Normal file
33
src/html.rs
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
use html_parser::Dom;
|
||||
use reqwest::blocking::get;
|
||||
use std::error::Error;
|
||||
|
||||
/// Lädt die HTML-Seite von der angegebenen URL herunter und konvertiert sie in JSON
|
||||
pub fn fetch_and_parse_html_to_json(url: &str) -> Result<String, Box<dyn Error>> {
|
||||
// HTML herunterladen
|
||||
let response = get(url)?;
|
||||
if !response.status().is_success() {
|
||||
return Err(format!("Fehler beim Abrufen der URL {}: {}", url, response.status()).into());
|
||||
}
|
||||
|
||||
let body = response.text()?;
|
||||
|
||||
// HTML parsen
|
||||
let dom = Dom::parse(&body)?;
|
||||
|
||||
// In JSON konvertieren
|
||||
let json = dom.to_json_pretty()?;
|
||||
Ok(json)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_fetch_and_parse() {
|
||||
let url = "https://www.linuxfromscratch.org/~thomas/multilib-m32/chapter02/hostreqs.html";
|
||||
let json = fetch_and_parse_html_to_json(url).expect("Fehler beim Parsen");
|
||||
assert!(json.contains("Host System Requirements"));
|
||||
}
|
||||
}
|
||||
40
src/main.rs
40
src/main.rs
|
|
@ -1,11 +1,12 @@
|
|||
mod downloader;
|
||||
mod html;
|
||||
mod md5_utils;
|
||||
mod mirrors;
|
||||
mod version_check;
|
||||
mod wget_list;
|
||||
|
||||
#[cfg(feature = "tui")]
|
||||
mod tui; // Importiere das TUI-Modul, wenn das Feature aktiv ist
|
||||
mod tui;
|
||||
|
||||
use console::style;
|
||||
use rand::Rng;
|
||||
|
|
@ -16,34 +17,30 @@ 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-Modus
|
||||
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!(
|
||||
"{} Host system does not meet minimum requirements. Exiting.",
|
||||
style("❌").red().bold()
|
||||
);
|
||||
std::process::exit(1);
|
||||
}
|
||||
// --- Dynamische Version-Prüfung aus HTML ---
|
||||
let json = html::fetch_and_parse_html_to_json(
|
||||
"https://www.linuxfromscratch.org/~thomas/multilib-m32/chapter02/hostreqs.html",
|
||||
)?;
|
||||
version_check::run_version_checks_from_json(&json);
|
||||
|
||||
println!(
|
||||
"{} All version checks passed. Starting downloader...",
|
||||
style("✅").green().bold()
|
||||
);
|
||||
|
||||
// --- Determine LFS sources path ---
|
||||
// --- Bestimme LFS-Sources-Pfad ---
|
||||
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 mut rng = rand::thread_rng();
|
||||
let random_number: u32 = rng.gen_range(1000..=9999);
|
||||
let tmp_path = format!("/tmp/lfs_{}", random_number);
|
||||
println!(
|
||||
"{} Using temporary path {}",
|
||||
|
|
@ -54,20 +51,13 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||
}
|
||||
};
|
||||
|
||||
// --- 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.
|
||||
// --- CLI Mirror-Auswahl: default oder erweiterbar ---
|
||||
let package_mirror: Option<String> = None;
|
||||
|
||||
|
||||
// --- Hole wget-Liste ---
|
||||
let wget_list = wget_list::get_wget_list()?;
|
||||
|
||||
// --- Prepare MD5 map ---
|
||||
// --- Bereite MD5-Map vor ---
|
||||
let mut md5_map: HashMap<String, String> = HashMap::new();
|
||||
let md5_content = md5_utils::get_md5sums()?;
|
||||
for line in md5_content.lines() {
|
||||
|
|
@ -77,7 +67,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||
}
|
||||
}
|
||||
|
||||
// --- Download files ---
|
||||
// --- Lade Dateien herunter ---
|
||||
downloader::download_files(&wget_list, &lfs_sources, package_mirror, Some(&md5_map))?;
|
||||
|
||||
println!("{} All done!", style("🎉").green().bold());
|
||||
|
|
|
|||
|
|
@ -3,6 +3,29 @@ use reqwest::blocking::Client;
|
|||
use scraper::{Html, Selector};
|
||||
use std::io::{self, Write};
|
||||
|
||||
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")
|
||||
.send()?
|
||||
.text()?;
|
||||
let document = Html::parse_document(&res);
|
||||
let selector = Selector::parse("a[href^='http']").unwrap();
|
||||
let mirrors = document
|
||||
.select(&selector)
|
||||
.filter_map(|element| {
|
||||
let href = element.value().attr("href")?;
|
||||
// Basic filtering to get potential mirror URLs
|
||||
if href.contains("ftp.gnu.org") || href.contains("mirror") {
|
||||
Some(href.to_string())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
Ok(mirrors)
|
||||
}
|
||||
|
||||
pub fn choose_package_mirror() -> Option<String> {
|
||||
let mirrors = match fetch_mirrors() {
|
||||
Ok(mirrors) => mirrors,
|
||||
|
|
@ -42,26 +65,3 @@ pub fn choose_package_mirror() -> Option<String> {
|
|||
Some(chosen.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
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")
|
||||
.send()?
|
||||
.text()?;
|
||||
let document = Html::parse_document(&res);
|
||||
let selector = Selector::parse("a[href^='http']").unwrap();
|
||||
let mirrors = document
|
||||
.select(&selector)
|
||||
.filter_map(|element| {
|
||||
let href = element.value().attr("href")?;
|
||||
// Basic filtering to get potential mirror URLs
|
||||
if href.contains("ftp.gnu.org") || href.contains("mirror") {
|
||||
Some(href.to_string())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
Ok(mirrors)
|
||||
}
|
||||
|
|
|
|||
165
src/tui.rs
165
src/tui.rs
|
|
@ -1,4 +1,6 @@
|
|||
#[cfg(feature = "tui")]
|
||||
use crate::html::fetch_and_parse_html_to_json;
|
||||
#[cfg(feature = "tui")]
|
||||
use crate::{downloader, md5_utils, mirrors, wget_list};
|
||||
#[cfg(feature = "tui")]
|
||||
use crossterm::{
|
||||
|
|
@ -7,6 +9,8 @@ use crossterm::{
|
|||
terminal::{EnterAlternateScreen, LeaveAlternateScreen, disable_raw_mode, enable_raw_mode},
|
||||
};
|
||||
#[cfg(feature = "tui")]
|
||||
use rand::Rng;
|
||||
#[cfg(feature = "tui")]
|
||||
use ratatui::{
|
||||
Terminal,
|
||||
backend::CrosstermBackend,
|
||||
|
|
@ -17,25 +21,47 @@ use ratatui::{
|
|||
#[cfg(feature = "tui")]
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
fs,
|
||||
io::stdout,
|
||||
path::PathBuf,
|
||||
sync::{Arc, Mutex},
|
||||
thread,
|
||||
time::{Duration, Instant},
|
||||
fs, // Added for file system operations
|
||||
};
|
||||
#[cfg(feature = "tui")] // Hinzugefügt: Import des Rng-Traits
|
||||
use rand::Rng;
|
||||
|
||||
#[cfg(feature = "tui")]
|
||||
use reqwest::blocking::get;
|
||||
#[cfg(feature = "tui")]
|
||||
use scraper::{Html, Selector};
|
||||
#[cfg(feature = "tui")]
|
||||
use serde_json::json;
|
||||
|
||||
// ----------------- HTML fetch function -----------------
|
||||
#[cfg(feature = "tui")]
|
||||
fn fetch_html_to_json(url: &str) -> Result<String, Box<dyn std::error::Error>> {
|
||||
let body = get(url)?.text()?;
|
||||
let document = Html::parse_document(&body);
|
||||
|
||||
let selector = Selector::parse("body").unwrap();
|
||||
let mut contents = vec![];
|
||||
for element in document.select(&selector) {
|
||||
contents.push(element.text().collect::<Vec<_>>().join(" "));
|
||||
}
|
||||
|
||||
Ok(serde_json::to_string_pretty(
|
||||
&json!({ "body_text": contents }),
|
||||
)?)
|
||||
}
|
||||
|
||||
// ----------------- TUI functions -----------------
|
||||
#[cfg(feature = "tui")]
|
||||
fn init_environment() -> Result<PathBuf, Box<dyn std::error::Error>> {
|
||||
let mut rng = rand::rng();
|
||||
let mut rng = rand::thread_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)?;
|
||||
|
||||
fs::create_dir_all(&lfs_sources_path)?;
|
||||
Ok(lfs_sources_path)
|
||||
}
|
||||
|
||||
|
|
@ -58,7 +84,7 @@ fn select_mirrors_tui(mirrors: Vec<String>) -> Vec<String> {
|
|||
loop {
|
||||
terminal
|
||||
.draw(|f| {
|
||||
let size = f.area();
|
||||
let size = f.size();
|
||||
let items: Vec<ListItem> = mirrors
|
||||
.iter()
|
||||
.enumerate()
|
||||
|
|
@ -70,8 +96,8 @@ fn select_mirrors_tui(mirrors: Vec<String>) -> Vec<String> {
|
|||
let list = List::new(items)
|
||||
.block(
|
||||
Block::default()
|
||||
.borders(Borders::ALL)
|
||||
.title("Select mirrors"),
|
||||
.title("Select mirrors")
|
||||
.borders(Borders::ALL),
|
||||
)
|
||||
.highlight_symbol(">> ");
|
||||
f.render_stateful_widget(list, size, &mut state);
|
||||
|
|
@ -182,7 +208,7 @@ fn download_packages_tui(lfs_sources: &PathBuf) {
|
|||
loop {
|
||||
terminal
|
||||
.draw(|f| {
|
||||
let size = f.area();
|
||||
let size = f.size();
|
||||
let items: Vec<ListItem> = {
|
||||
let state = download_state.lock().unwrap();
|
||||
state
|
||||
|
|
@ -210,6 +236,26 @@ fn download_packages_tui(lfs_sources: &PathBuf) {
|
|||
execute!(terminal.backend_mut(), LeaveAlternateScreen).unwrap();
|
||||
}
|
||||
|
||||
// ----------------- Cleanup function -----------------
|
||||
#[cfg(feature = "tui")]
|
||||
fn cleanup_temp_directories() -> Result<usize, Box<dyn std::error::Error>> {
|
||||
let mut cleaned_count = 0;
|
||||
for entry in fs::read_dir("/tmp")? {
|
||||
let entry = entry?;
|
||||
let path = entry.path();
|
||||
if path.is_dir() {
|
||||
if let Some(name) = path.file_name().and_then(|s| s.to_str()) {
|
||||
if name.starts_with("lfs_") {
|
||||
fs::remove_dir_all(&path)?;
|
||||
cleaned_count += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(cleaned_count)
|
||||
}
|
||||
|
||||
// ----------------- Main TUI menu -----------------
|
||||
#[cfg(feature = "tui")]
|
||||
pub fn tui_menu() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let mut stdout = stdout();
|
||||
|
|
@ -222,19 +268,20 @@ pub fn tui_menu() -> Result<(), Box<dyn std::error::Error>> {
|
|||
"🌱 Init environment",
|
||||
"📦 Download packages",
|
||||
"🔍 Check status",
|
||||
"🧹 Clean up temp directories", // Neuer Menüpunkt
|
||||
"🧹 Clean up temp directories",
|
||||
"📄 Test JSON fetch", // NEW BUTTON
|
||||
"❌ 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
|
||||
let mut status_message: Option<String> = None;
|
||||
let mut status_message_timer: Option<Instant> = None;
|
||||
|
||||
loop {
|
||||
terminal.draw(|f| {
|
||||
let size = f.area();
|
||||
let size = f.size();
|
||||
let block = Block::default()
|
||||
.title("✨ lpkg TUI 🌈")
|
||||
.borders(Borders::ALL);
|
||||
|
|
@ -256,34 +303,35 @@ pub fn tui_menu() -> Result<(), Box<dyn std::error::Error>> {
|
|||
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 paragraph = Paragraph::new(msg.as_str()).block(
|
||||
Block::default()
|
||||
.borders(Borders::NONE)
|
||||
.style(Style::default().fg(Color::Yellow)),
|
||||
);
|
||||
let msg_area = Layout::default()
|
||||
.direction(Direction::Horizontal)
|
||||
.constraints([Constraint::Percentage(10), Constraint::Percentage(80), Constraint::Percentage(10)])
|
||||
.split(size)[1]; // Mittlerer Bereich
|
||||
.constraints([
|
||||
Constraint::Percentage(10),
|
||||
Constraint::Percentage(80),
|
||||
Constraint::Percentage(10),
|
||||
])
|
||||
.split(size)[1];
|
||||
let msg_area = Layout::default()
|
||||
.direction(Direction::Vertical)
|
||||
.constraints([Constraint::Min(0), Constraint::Length(1)])
|
||||
.split(msg_area)[1]; // Unterste Zeile
|
||||
.split(msg_area)[1];
|
||||
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
|
||||
if timer.elapsed() > Duration::from_secs(3) {
|
||||
status_message = None;
|
||||
status_message_timer = None;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if let Event::Key(key) = event::read()? {
|
||||
match key.code {
|
||||
KeyCode::Down => {
|
||||
|
|
@ -300,44 +348,61 @@ pub fn tui_menu() -> Result<(), Box<dyn std::error::Error>> {
|
|||
}
|
||||
KeyCode::Enter => match state.selected() {
|
||||
Some(0) => {
|
||||
// Init environment
|
||||
match init_environment() {
|
||||
Ok(path) => {
|
||||
lfs_sources = Some(path.clone());
|
||||
status_message = Some(format!("✅ Environment initialized: {}", path.display()));
|
||||
status_message =
|
||||
Some(format!("✅ Environment initialized: {}", path.display()));
|
||||
}
|
||||
Err(e) => {
|
||||
status_message = Some(format!("❌ Failed to initialize environment: {}", e));
|
||||
status_message =
|
||||
Some(format!("❌ Failed to init environment: {}", e));
|
||||
}
|
||||
}
|
||||
status_message_timer = Some(std::time::Instant::now());
|
||||
status_message_timer = Some(Instant::now());
|
||||
}
|
||||
Some(1) => {
|
||||
if let Some(ref path) = lfs_sources {
|
||||
// Download packages
|
||||
if let Some(path) = &lfs_sources {
|
||||
download_packages_tui(path);
|
||||
} else {
|
||||
status_message = Some("⚠️ Please initialize environment first!".to_string());
|
||||
status_message_timer = Some(std::time::Instant::now());
|
||||
}
|
||||
}
|
||||
Some(2) => {
|
||||
// Status
|
||||
status_message = Some("🔍 Status selected! (TODO)".to_string());
|
||||
status_message_timer = Some(std::time::Instant::now());
|
||||
status_message_timer = Some(Instant::now());
|
||||
}
|
||||
Some(3) => {
|
||||
// Aufruf der neuen Bereinigungsfunktion
|
||||
status_message = Some("🧹 Cleaning up temporary directories...".to_string());
|
||||
status_message_timer = Some(std::time::Instant::now());
|
||||
// Cleanup
|
||||
match cleanup_temp_directories() {
|
||||
Ok(count) => {
|
||||
status_message = Some(format!("✅ Cleaned up {} temporary directories.", count));
|
||||
status_message =
|
||||
Some(format!("✅ Cleaned {} temporary dirs", count));
|
||||
}
|
||||
Err(e) => {
|
||||
status_message = Some(format!("❌ Failed to clean up: {}", e));
|
||||
status_message = Some(format!("❌ Failed cleanup: {}", e));
|
||||
}
|
||||
}
|
||||
status_message_timer = Some(std::time::Instant::now());
|
||||
status_message_timer = Some(Instant::now());
|
||||
}
|
||||
Some(4) | _ => break,
|
||||
Some(4) => {
|
||||
// Test JSON fetch
|
||||
// Leave TUI first
|
||||
disable_raw_mode()?;
|
||||
execute!(terminal.backend_mut(), LeaveAlternateScreen)?;
|
||||
terminal.show_cursor()?;
|
||||
|
||||
match fetch_and_parse_html_to_json(
|
||||
"https://www.linuxfromscratch.org/~thomas/multilib-m32/chapter02/hostreqs.html",
|
||||
) {
|
||||
Ok(json) => println!("✅ JSON output:\n{}", json),
|
||||
Err(e) => eprintln!("❌ Error: {}", e),
|
||||
}
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
Some(5) | _ => break,
|
||||
},
|
||||
KeyCode::Esc => break,
|
||||
_ => {}
|
||||
|
|
@ -350,21 +415,3 @@ 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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,9 @@
|
|||
use reqwest;
|
||||
use scraper::{Html, Selector};
|
||||
use std::process::Command;
|
||||
|
||||
pub fn run_command(cmd: &str, args: &[&str]) -> Option<String> {
|
||||
/// Führt ein Kommando aus und gibt die erste Zeile der Version zurück
|
||||
fn run_command(cmd: &str, args: &[&str]) -> Option<String> {
|
||||
let output = Command::new(cmd).args(args).output().ok()?;
|
||||
if output.status.success() {
|
||||
Some(String::from_utf8_lossy(&output.stdout).trim().to_string())
|
||||
|
|
@ -9,7 +12,8 @@ pub fn run_command(cmd: &str, args: &[&str]) -> Option<String> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn check_version(installed: &str, required: &str) -> bool {
|
||||
/// Vergleicht zwei Versionen
|
||||
fn check_version(installed: &str, required: &str) -> bool {
|
||||
let parse_ver = |v: &str| {
|
||||
v.split(['.', '-'])
|
||||
.filter_map(|s| s.parse::<u32>().ok())
|
||||
|
|
@ -29,8 +33,9 @@ pub fn check_version(installed: &str, required: &str) -> bool {
|
|||
i.len() >= r.len()
|
||||
}
|
||||
|
||||
pub fn ver_check(program: &str, arg: &str, min_version: &str) -> bool {
|
||||
match run_command(program, &[arg]) {
|
||||
/// Führt eine Version-Prüfung durch
|
||||
fn ver_check(program: &str, cmd: &str, min_version: &str) -> bool {
|
||||
match run_command(cmd, &["--version"]) {
|
||||
Some(output) => {
|
||||
let ver = output
|
||||
.lines()
|
||||
|
|
@ -57,27 +62,83 @@ pub fn ver_check(program: &str, arg: &str, min_version: &str) -> bool {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn run_version_checks() -> bool {
|
||||
/// Führt die Kernel-Prüfung durch
|
||||
fn ver_kernel(min_version: &str) -> bool {
|
||||
let kernel = run_command("uname", &["-r"]).unwrap_or_default();
|
||||
if check_version(&kernel, min_version) {
|
||||
println!("OK: Linux Kernel {} >= {}", kernel, min_version);
|
||||
true
|
||||
} else {
|
||||
println!(
|
||||
"ERROR: Linux Kernel {} is too old ({} required)",
|
||||
kernel, min_version
|
||||
);
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/// Lädt die LFS-Seite und führt alle Versionsprüfungen aus
|
||||
pub fn run_version_checks_from_html(url: &str) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let html_text = reqwest::blocking::get(url)?.text()?;
|
||||
let document = Html::parse_document(&html_text);
|
||||
let selector = Selector::parse("pre").unwrap();
|
||||
|
||||
let mut ok = true;
|
||||
|
||||
ok &= ver_check("bash", "--version", "3.2");
|
||||
ok &= ver_check("gcc", "--version", "5.4");
|
||||
ok &= ver_check("make", "--version", "4.0");
|
||||
ok &= ver_check("tar", "--version", "1.22");
|
||||
for element in document.select(&selector) {
|
||||
let pre_text = element.text().collect::<Vec<_>>().join("\n");
|
||||
|
||||
// Kernel check
|
||||
if let Some(kernel) = run_command("uname", &["-r"]) {
|
||||
if check_version(&kernel, "5.4") {
|
||||
println!("OK: Linux Kernel {} >= 5.4", kernel);
|
||||
} else {
|
||||
println!("ERROR: Linux Kernel {} is too old (5.4 required)", kernel);
|
||||
ok = false;
|
||||
for line in pre_text.lines() {
|
||||
let line = line.trim();
|
||||
if line.starts_with("ver_check") {
|
||||
let parts: Vec<&str> = line.split_whitespace().collect();
|
||||
if parts.len() >= 4 {
|
||||
let prog = parts[1];
|
||||
let cmd = parts[2];
|
||||
let ver = parts[3];
|
||||
ok &= ver_check(prog, cmd, ver);
|
||||
}
|
||||
} else if line.starts_with("ver_kernel") {
|
||||
if let Some(ver) = line.split_whitespace().nth(1) {
|
||||
ok &= ver_kernel(ver);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// CPU cores
|
||||
let cores = num_cpus::get();
|
||||
println!("OK: {} logical cores available", cores);
|
||||
// Alias-Checks
|
||||
let alias_check = |cmd: &str, expected: &str| {
|
||||
if let Some(output) = run_command(cmd, &["--version"]) {
|
||||
if output.to_lowercase().contains(&expected.to_lowercase()) {
|
||||
println!("OK: {:<4} is {}", cmd, expected);
|
||||
} else {
|
||||
println!("ERROR: {:<4} is NOT {}", cmd, expected);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
ok
|
||||
alias_check("awk", "GNU");
|
||||
alias_check("yacc", "Bison");
|
||||
alias_check("sh", "Bash");
|
||||
|
||||
// Compiler-Test
|
||||
if run_command("g++", &["-x", "c++", "-"]).is_some() {
|
||||
println!("OK: g++ works");
|
||||
} else {
|
||||
println!("ERROR: g++ does NOT work");
|
||||
}
|
||||
|
||||
// nproc-Test
|
||||
let nproc = run_command("nproc", &[]).unwrap_or_default();
|
||||
if nproc.is_empty() {
|
||||
println!("ERROR: nproc is not available or empty");
|
||||
} else {
|
||||
println!("OK: nproc reports {} logical cores available", nproc);
|
||||
}
|
||||
|
||||
if !ok {
|
||||
println!("Some version checks failed.");
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue