working
This commit is contained in:
parent
23905877c7
commit
208234df27
7 changed files with 478 additions and 127 deletions
217
Cargo.lock
generated
217
Cargo.lock
generated
|
|
@ -30,6 +30,15 @@ dependencies = [
|
||||||
"zerocopy",
|
"zerocopy",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "aho-corasick"
|
||||||
|
version = "1.1.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "allocator-api2"
|
name = "allocator-api2"
|
||||||
version = "0.2.21"
|
version = "0.2.21"
|
||||||
|
|
@ -131,6 +140,15 @@ version = "2.9.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2261d10cca569e4643e526d8dc2e62e433cc8aba21ab764233731f8d369bf394"
|
checksum = "2261d10cca569e4643e526d8dc2e62e433cc8aba21ab764233731f8d369bf394"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "block-buffer"
|
||||||
|
version = "0.10.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
|
||||||
|
dependencies = [
|
||||||
|
"generic-array",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bumpalo"
|
name = "bumpalo"
|
||||||
version = "3.19.0"
|
version = "3.19.0"
|
||||||
|
|
@ -278,6 +296,15 @@ version = "0.8.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
|
checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cpufeatures"
|
||||||
|
version = "0.2.17"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crossterm"
|
name = "crossterm"
|
||||||
version = "0.28.1"
|
version = "0.28.1"
|
||||||
|
|
@ -321,6 +348,16 @@ dependencies = [
|
||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crypto-common"
|
||||||
|
version = "0.1.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
|
||||||
|
dependencies = [
|
||||||
|
"generic-array",
|
||||||
|
"typenum",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cssparser"
|
name = "cssparser"
|
||||||
version = "0.31.2"
|
version = "0.31.2"
|
||||||
|
|
@ -411,6 +448,16 @@ dependencies = [
|
||||||
"syn 2.0.106",
|
"syn 2.0.106",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "digest"
|
||||||
|
version = "0.10.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
|
||||||
|
dependencies = [
|
||||||
|
"block-buffer",
|
||||||
|
"crypto-common",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "displaydoc"
|
name = "displaydoc"
|
||||||
version = "0.2.5"
|
version = "0.2.5"
|
||||||
|
|
@ -422,6 +469,12 @@ dependencies = [
|
||||||
"syn 2.0.106",
|
"syn 2.0.106",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "doc-comment"
|
||||||
|
version = "0.3.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "document-features"
|
name = "document-features"
|
||||||
version = "0.2.11"
|
version = "0.2.11"
|
||||||
|
|
@ -606,6 +659,16 @@ dependencies = [
|
||||||
"byteorder",
|
"byteorder",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "generic-array"
|
||||||
|
version = "0.14.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
|
||||||
|
dependencies = [
|
||||||
|
"typenum",
|
||||||
|
"version_check",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "getopts"
|
name = "getopts"
|
||||||
version = "0.2.24"
|
version = "0.2.24"
|
||||||
|
|
@ -712,6 +775,21 @@ dependencies = [
|
||||||
"syn 2.0.106",
|
"syn 2.0.106",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "html_parser"
|
||||||
|
version = "0.7.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f6f56db07b6612644f6f7719f8ef944f75fff9d6378fdf3d316fd32194184abd"
|
||||||
|
dependencies = [
|
||||||
|
"doc-comment",
|
||||||
|
"pest",
|
||||||
|
"pest_derive",
|
||||||
|
"serde",
|
||||||
|
"serde_derive",
|
||||||
|
"serde_json",
|
||||||
|
"thiserror 1.0.69",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "http"
|
name = "http"
|
||||||
version = "1.3.1"
|
version = "1.3.1"
|
||||||
|
|
@ -1277,14 +1355,17 @@ dependencies = [
|
||||||
"clap",
|
"clap",
|
||||||
"console",
|
"console",
|
||||||
"crossterm 0.29.0",
|
"crossterm 0.29.0",
|
||||||
|
"html_parser",
|
||||||
"indicatif",
|
"indicatif",
|
||||||
"md5",
|
"md5",
|
||||||
"num_cpus",
|
"num_cpus",
|
||||||
"rand 0.9.2",
|
"rand 0.9.2",
|
||||||
"ratatui",
|
"ratatui",
|
||||||
|
"regex",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
"scraper",
|
"scraper",
|
||||||
"serde",
|
"serde",
|
||||||
|
"serde_json",
|
||||||
"spinners",
|
"spinners",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
@ -1323,6 +1404,50 @@ version = "2.3.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220"
|
checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pest"
|
||||||
|
version = "2.8.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "21e0a3a33733faeaf8651dfee72dd0f388f0c8e5ad496a3478fa5a922f49cfa8"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
"thiserror 2.0.17",
|
||||||
|
"ucd-trie",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pest_derive"
|
||||||
|
version = "2.8.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bc58706f770acb1dbd0973e6530a3cff4746fb721207feb3a8a6064cd0b6c663"
|
||||||
|
dependencies = [
|
||||||
|
"pest",
|
||||||
|
"pest_generator",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pest_generator"
|
||||||
|
version = "2.8.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6d4f36811dfe07f7b8573462465d5cb8965fffc2e71ae377a33aecf14c2c9a2f"
|
||||||
|
dependencies = [
|
||||||
|
"pest",
|
||||||
|
"pest_meta",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.106",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pest_meta"
|
||||||
|
version = "2.8.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "42919b05089acbd0a5dcd5405fb304d17d1053847b81163d09c4ad18ce8e8420"
|
||||||
|
dependencies = [
|
||||||
|
"pest",
|
||||||
|
"sha2",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "phf"
|
name = "phf"
|
||||||
version = "0.10.1"
|
version = "0.10.1"
|
||||||
|
|
@ -1574,6 +1699,35 @@ dependencies = [
|
||||||
"bitflags",
|
"bitflags",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex"
|
||||||
|
version = "1.11.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8b5288124840bee7b386bc413c487869b360b2b4ec421ea56425128692f2a82c"
|
||||||
|
dependencies = [
|
||||||
|
"aho-corasick",
|
||||||
|
"memchr",
|
||||||
|
"regex-automata",
|
||||||
|
"regex-syntax",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex-automata"
|
||||||
|
version = "0.4.11"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "833eb9ce86d40ef33cb1306d8accf7bc8ec2bfea4355cbdebb3df68b40925cad"
|
||||||
|
dependencies = [
|
||||||
|
"aho-corasick",
|
||||||
|
"memchr",
|
||||||
|
"regex-syntax",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex-syntax"
|
||||||
|
version = "0.8.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "caf4aa5b0f434c91fe5c7f1ecb6a5ece2130b02ad2a590589dda5146df959001"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "reqwest"
|
name = "reqwest"
|
||||||
version = "0.12.23"
|
version = "0.12.23"
|
||||||
|
|
@ -1844,6 +1998,17 @@ dependencies = [
|
||||||
"stable_deref_trait",
|
"stable_deref_trait",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sha2"
|
||||||
|
version = "0.10.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"cpufeatures",
|
||||||
|
"digest",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "shlex"
|
name = "shlex"
|
||||||
version = "1.3.0"
|
version = "1.3.0"
|
||||||
|
|
@ -2105,6 +2270,46 @@ dependencies = [
|
||||||
"utf-8",
|
"utf-8",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thiserror"
|
||||||
|
version = "1.0.69"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52"
|
||||||
|
dependencies = [
|
||||||
|
"thiserror-impl 1.0.69",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thiserror"
|
||||||
|
version = "2.0.17"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8"
|
||||||
|
dependencies = [
|
||||||
|
"thiserror-impl 2.0.17",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thiserror-impl"
|
||||||
|
version = "1.0.69"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.106",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thiserror-impl"
|
||||||
|
version = "2.0.17"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.106",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tinystr"
|
name = "tinystr"
|
||||||
version = "0.8.1"
|
version = "0.8.1"
|
||||||
|
|
@ -2235,6 +2440,18 @@ version = "0.2.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
|
checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "typenum"
|
||||||
|
version = "1.18.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ucd-trie"
|
||||||
|
version = "0.1.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-ident"
|
name = "unicode-ident"
|
||||||
version = "1.0.19"
|
version = "1.0.19"
|
||||||
|
|
|
||||||
|
|
@ -8,15 +8,18 @@ anyhow = "1.0.100"
|
||||||
clap = { version = "4.5.48", features= ["derive"] }
|
clap = { version = "4.5.48", features= ["derive"] }
|
||||||
console = "0.16.1"
|
console = "0.16.1"
|
||||||
crossterm = { version = "0.29.0", optional = true }
|
crossterm = { version = "0.29.0", optional = true }
|
||||||
|
html_parser = "0.7.0"
|
||||||
indicatif = "0.18.0"
|
indicatif = "0.18.0"
|
||||||
md5 = "0.8.0"
|
md5 = "0.8.0"
|
||||||
num_cpus = "1.17.0"
|
num_cpus = "1.17.0"
|
||||||
rand = "0.9.2"
|
rand = "0.9.2"
|
||||||
ratatui = { version = "0.29.0", optional = true }
|
ratatui = { version = "0.29.0", optional = true }
|
||||||
|
regex = "1.11.3"
|
||||||
reqwest = { version = "0.12.23", features = ["blocking", "json"] }
|
reqwest = { version = "0.12.23", features = ["blocking", "json"] }
|
||||||
scraper = "0.19.0"
|
scraper = "0.19.0"
|
||||||
|
|
||||||
serde = { version = "1.0.228", features = ["derive"] }
|
serde = { version = "1.0.228", features = ["derive"] }
|
||||||
|
serde_json = "1.0.145"
|
||||||
spinners = "4.1.1"
|
spinners = "4.1.1"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
|
|
|
||||||
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 downloader;
|
||||||
|
mod html;
|
||||||
mod md5_utils;
|
mod md5_utils;
|
||||||
mod mirrors;
|
mod mirrors;
|
||||||
mod version_check;
|
mod version_check;
|
||||||
mod wget_list;
|
mod wget_list;
|
||||||
|
|
||||||
#[cfg(feature = "tui")]
|
#[cfg(feature = "tui")]
|
||||||
mod tui; // Importiere das TUI-Modul, wenn das Feature aktiv ist
|
mod tui;
|
||||||
|
|
||||||
use console::style;
|
use console::style;
|
||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
|
|
@ -16,34 +17,30 @@ use std::path::PathBuf;
|
||||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
#[cfg(feature = "tui")]
|
#[cfg(feature = "tui")]
|
||||||
{
|
{
|
||||||
// Wenn das TUI-Feature aktiv ist, starte das TUI-Menü
|
// TUI-Modus
|
||||||
tui::tui_menu()?;
|
tui::tui_menu()?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "tui"))]
|
#[cfg(not(feature = "tui"))]
|
||||||
{
|
{
|
||||||
// Wenn das TUI-Feature NICHT aktiv ist, führe die CLI-Logik aus
|
// --- Dynamische Version-Prüfung aus HTML ---
|
||||||
// --- Run host system version checks ---
|
let json = html::fetch_and_parse_html_to_json(
|
||||||
if version_check::run_version_checks() {
|
"https://www.linuxfromscratch.org/~thomas/multilib-m32/chapter02/hostreqs.html",
|
||||||
eprintln!(
|
)?;
|
||||||
"{} Host system does not meet minimum requirements. Exiting.",
|
version_check::run_version_checks_from_json(&json);
|
||||||
style("❌").red().bold()
|
|
||||||
);
|
|
||||||
std::process::exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
println!(
|
println!(
|
||||||
"{} All version checks passed. Starting downloader...",
|
"{} All version checks passed. Starting downloader...",
|
||||||
style("✅").green().bold()
|
style("✅").green().bold()
|
||||||
);
|
);
|
||||||
|
|
||||||
// --- Determine LFS sources path ---
|
// --- Bestimme LFS-Sources-Pfad ---
|
||||||
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::thread_rng(); // Verwende thread_rng() statt rng()
|
let mut rng = rand::thread_rng();
|
||||||
let random_number: u32 = rng.gen_range(1000..=9999); // Verwende gen_range() statt random_range()
|
let random_number: u32 = rng.gen_range(1000..=9999);
|
||||||
let tmp_path = format!("/tmp/lfs_{}", random_number);
|
let tmp_path = format!("/tmp/lfs_{}", random_number);
|
||||||
println!(
|
println!(
|
||||||
"{} Using temporary path {}",
|
"{} Using temporary path {}",
|
||||||
|
|
@ -54,20 +51,13 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// --- Choose mirror and fetch wget list ---
|
// --- CLI Mirror-Auswahl: default oder erweiterbar ---
|
||||||
// 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 package_mirror: Option<String> = None;
|
||||||
|
|
||||||
|
// --- Hole wget-Liste ---
|
||||||
let wget_list = wget_list::get_wget_list()?;
|
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 mut md5_map: HashMap<String, String> = HashMap::new();
|
||||||
let md5_content = md5_utils::get_md5sums()?;
|
let md5_content = md5_utils::get_md5sums()?;
|
||||||
for line in md5_content.lines() {
|
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))?;
|
downloader::download_files(&wget_list, &lfs_sources, package_mirror, Some(&md5_map))?;
|
||||||
|
|
||||||
println!("{} All done!", style("🎉").green().bold());
|
println!("{} All done!", style("🎉").green().bold());
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,29 @@ use reqwest::blocking::Client;
|
||||||
use scraper::{Html, Selector};
|
use scraper::{Html, Selector};
|
||||||
use std::io::{self, Write};
|
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> {
|
pub fn choose_package_mirror() -> Option<String> {
|
||||||
let mirrors = match fetch_mirrors() {
|
let mirrors = match fetch_mirrors() {
|
||||||
Ok(mirrors) => mirrors,
|
Ok(mirrors) => mirrors,
|
||||||
|
|
@ -42,26 +65,3 @@ pub fn choose_package_mirror() -> Option<String> {
|
||||||
Some(chosen.to_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")]
|
#[cfg(feature = "tui")]
|
||||||
|
use crate::html::fetch_and_parse_html_to_json;
|
||||||
|
#[cfg(feature = "tui")]
|
||||||
use crate::{downloader, md5_utils, mirrors, wget_list};
|
use crate::{downloader, md5_utils, mirrors, wget_list};
|
||||||
#[cfg(feature = "tui")]
|
#[cfg(feature = "tui")]
|
||||||
use crossterm::{
|
use crossterm::{
|
||||||
|
|
@ -7,6 +9,8 @@ use crossterm::{
|
||||||
terminal::{EnterAlternateScreen, LeaveAlternateScreen, disable_raw_mode, enable_raw_mode},
|
terminal::{EnterAlternateScreen, LeaveAlternateScreen, disable_raw_mode, enable_raw_mode},
|
||||||
};
|
};
|
||||||
#[cfg(feature = "tui")]
|
#[cfg(feature = "tui")]
|
||||||
|
use rand::Rng;
|
||||||
|
#[cfg(feature = "tui")]
|
||||||
use ratatui::{
|
use ratatui::{
|
||||||
Terminal,
|
Terminal,
|
||||||
backend::CrosstermBackend,
|
backend::CrosstermBackend,
|
||||||
|
|
@ -17,25 +21,47 @@ use ratatui::{
|
||||||
#[cfg(feature = "tui")]
|
#[cfg(feature = "tui")]
|
||||||
use std::{
|
use std::{
|
||||||
collections::HashMap,
|
collections::HashMap,
|
||||||
|
fs,
|
||||||
io::stdout,
|
io::stdout,
|
||||||
path::PathBuf,
|
path::PathBuf,
|
||||||
sync::{Arc, Mutex},
|
sync::{Arc, Mutex},
|
||||||
thread,
|
thread,
|
||||||
time::{Duration, Instant},
|
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")]
|
#[cfg(feature = "tui")]
|
||||||
fn init_environment() -> Result<PathBuf, Box<dyn std::error::Error>> {
|
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 random_number: u32 = rng.gen_range(1000..=9999);
|
||||||
let tmp_base_path = PathBuf::from(format!("/tmp/lfs_{}", random_number));
|
let tmp_base_path = PathBuf::from(format!("/tmp/lfs_{}", random_number));
|
||||||
let lfs_sources_path = tmp_base_path.join("sources");
|
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)
|
Ok(lfs_sources_path)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -58,7 +84,7 @@ fn select_mirrors_tui(mirrors: Vec<String>) -> Vec<String> {
|
||||||
loop {
|
loop {
|
||||||
terminal
|
terminal
|
||||||
.draw(|f| {
|
.draw(|f| {
|
||||||
let size = f.area();
|
let size = f.size();
|
||||||
let items: Vec<ListItem> = mirrors
|
let items: Vec<ListItem> = mirrors
|
||||||
.iter()
|
.iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
|
|
@ -70,8 +96,8 @@ fn select_mirrors_tui(mirrors: Vec<String>) -> Vec<String> {
|
||||||
let list = List::new(items)
|
let list = List::new(items)
|
||||||
.block(
|
.block(
|
||||||
Block::default()
|
Block::default()
|
||||||
.borders(Borders::ALL)
|
.title("Select mirrors")
|
||||||
.title("Select mirrors"),
|
.borders(Borders::ALL),
|
||||||
)
|
)
|
||||||
.highlight_symbol(">> ");
|
.highlight_symbol(">> ");
|
||||||
f.render_stateful_widget(list, size, &mut state);
|
f.render_stateful_widget(list, size, &mut state);
|
||||||
|
|
@ -182,7 +208,7 @@ fn download_packages_tui(lfs_sources: &PathBuf) {
|
||||||
loop {
|
loop {
|
||||||
terminal
|
terminal
|
||||||
.draw(|f| {
|
.draw(|f| {
|
||||||
let size = f.area();
|
let size = f.size();
|
||||||
let items: Vec<ListItem> = {
|
let items: Vec<ListItem> = {
|
||||||
let state = download_state.lock().unwrap();
|
let state = download_state.lock().unwrap();
|
||||||
state
|
state
|
||||||
|
|
@ -210,6 +236,26 @@ fn download_packages_tui(lfs_sources: &PathBuf) {
|
||||||
execute!(terminal.backend_mut(), LeaveAlternateScreen).unwrap();
|
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")]
|
#[cfg(feature = "tui")]
|
||||||
pub fn tui_menu() -> Result<(), Box<dyn std::error::Error>> {
|
pub fn tui_menu() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
let mut stdout = stdout();
|
let mut stdout = stdout();
|
||||||
|
|
@ -222,19 +268,20 @@ 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
|
"🧹 Clean up temp directories",
|
||||||
|
"📄 Test JSON fetch", // NEW BUTTON
|
||||||
"❌ 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: Option<String> = None;
|
||||||
let mut status_message_timer: Option<std::time::Instant> = None; // Für temporäre Meldungen
|
let mut status_message_timer: Option<Instant> = None;
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
terminal.draw(|f| {
|
terminal.draw(|f| {
|
||||||
let size = f.area();
|
let size = f.size();
|
||||||
let block = Block::default()
|
let block = Block::default()
|
||||||
.title("✨ lpkg TUI 🌈")
|
.title("✨ lpkg TUI 🌈")
|
||||||
.borders(Borders::ALL);
|
.borders(Borders::ALL);
|
||||||
|
|
@ -256,34 +303,35 @@ pub fn tui_menu() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
f.render_widget(list, chunks[i]);
|
f.render_widget(list, chunks[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Statusmeldung anzeigen
|
|
||||||
if let Some(msg) = &status_message {
|
if let Some(msg) = &status_message {
|
||||||
let msg_block = Block::default()
|
let paragraph = Paragraph::new(msg.as_str()).block(
|
||||||
.borders(Borders::NONE)
|
Block::default()
|
||||||
.style(Style::default().fg(Color::Yellow));
|
.borders(Borders::NONE)
|
||||||
let paragraph = Paragraph::new(msg.as_str()).block(msg_block);
|
.style(Style::default().fg(Color::Yellow)),
|
||||||
// Positionierung der Statusmeldung (z.B. unten in der Mitte)
|
);
|
||||||
let msg_area = Layout::default()
|
let msg_area = Layout::default()
|
||||||
.direction(Direction::Horizontal)
|
.direction(Direction::Horizontal)
|
||||||
.constraints([Constraint::Percentage(10), Constraint::Percentage(80), Constraint::Percentage(10)])
|
.constraints([
|
||||||
.split(size)[1]; // Mittlerer Bereich
|
Constraint::Percentage(10),
|
||||||
|
Constraint::Percentage(80),
|
||||||
|
Constraint::Percentage(10),
|
||||||
|
])
|
||||||
|
.split(size)[1];
|
||||||
let msg_area = Layout::default()
|
let msg_area = Layout::default()
|
||||||
.direction(Direction::Vertical)
|
.direction(Direction::Vertical)
|
||||||
.constraints([Constraint::Min(0), Constraint::Length(1)])
|
.constraints([Constraint::Min(0), Constraint::Length(1)])
|
||||||
.split(msg_area)[1]; // Unterste Zeile
|
.split(msg_area)[1];
|
||||||
f.render_widget(paragraph, msg_area);
|
f.render_widget(paragraph, msg_area);
|
||||||
}
|
}
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
// Statusmeldung nach einer bestimmten Zeit ausblenden
|
|
||||||
if let Some(timer) = status_message_timer {
|
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 = None;
|
||||||
status_message_timer = 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 => {
|
||||||
|
|
@ -300,44 +348,61 @@ pub fn tui_menu() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
}
|
}
|
||||||
KeyCode::Enter => match state.selected() {
|
KeyCode::Enter => match state.selected() {
|
||||||
Some(0) => {
|
Some(0) => {
|
||||||
|
// Init environment
|
||||||
match init_environment() {
|
match init_environment() {
|
||||||
Ok(path) => {
|
Ok(path) => {
|
||||||
lfs_sources = Some(path.clone());
|
lfs_sources = Some(path.clone());
|
||||||
status_message = Some(format!("✅ Environment initialized: {}", path.display()));
|
status_message =
|
||||||
|
Some(format!("✅ Environment initialized: {}", path.display()));
|
||||||
}
|
}
|
||||||
Err(e) => {
|
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) => {
|
Some(1) => {
|
||||||
if let Some(ref path) = lfs_sources {
|
// Download packages
|
||||||
|
if let Some(path) = &lfs_sources {
|
||||||
download_packages_tui(path);
|
download_packages_tui(path);
|
||||||
} else {
|
|
||||||
status_message = Some("⚠️ Please initialize environment first!".to_string());
|
|
||||||
status_message_timer = Some(std::time::Instant::now());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Some(2) => {
|
Some(2) => {
|
||||||
|
// Status
|
||||||
status_message = Some("🔍 Status selected! (TODO)".to_string());
|
status_message = Some("🔍 Status selected! (TODO)".to_string());
|
||||||
status_message_timer = Some(std::time::Instant::now());
|
status_message_timer = Some(Instant::now());
|
||||||
}
|
}
|
||||||
Some(3) => {
|
Some(3) => {
|
||||||
// Aufruf der neuen Bereinigungsfunktion
|
// Cleanup
|
||||||
status_message = Some("🧹 Cleaning up temporary directories...".to_string());
|
|
||||||
status_message_timer = Some(std::time::Instant::now());
|
|
||||||
match cleanup_temp_directories() {
|
match cleanup_temp_directories() {
|
||||||
Ok(count) => {
|
Ok(count) => {
|
||||||
status_message = Some(format!("✅ Cleaned up {} temporary directories.", count));
|
status_message =
|
||||||
|
Some(format!("✅ Cleaned {} temporary dirs", count));
|
||||||
}
|
}
|
||||||
Err(e) => {
|
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,
|
KeyCode::Esc => break,
|
||||||
_ => {}
|
_ => {}
|
||||||
|
|
@ -350,21 +415,3 @@ 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)
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,9 @@
|
||||||
|
use reqwest;
|
||||||
|
use scraper::{Html, Selector};
|
||||||
use std::process::Command;
|
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()?;
|
let output = Command::new(cmd).args(args).output().ok()?;
|
||||||
if output.status.success() {
|
if output.status.success() {
|
||||||
Some(String::from_utf8_lossy(&output.stdout).trim().to_string())
|
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| {
|
let parse_ver = |v: &str| {
|
||||||
v.split(['.', '-'])
|
v.split(['.', '-'])
|
||||||
.filter_map(|s| s.parse::<u32>().ok())
|
.filter_map(|s| s.parse::<u32>().ok())
|
||||||
|
|
@ -29,8 +33,9 @@ pub fn check_version(installed: &str, required: &str) -> bool {
|
||||||
i.len() >= r.len()
|
i.len() >= r.len()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn ver_check(program: &str, arg: &str, min_version: &str) -> bool {
|
/// Führt eine Version-Prüfung durch
|
||||||
match run_command(program, &[arg]) {
|
fn ver_check(program: &str, cmd: &str, min_version: &str) -> bool {
|
||||||
|
match run_command(cmd, &["--version"]) {
|
||||||
Some(output) => {
|
Some(output) => {
|
||||||
let ver = output
|
let ver = output
|
||||||
.lines()
|
.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;
|
let mut ok = true;
|
||||||
|
|
||||||
ok &= ver_check("bash", "--version", "3.2");
|
for element in document.select(&selector) {
|
||||||
ok &= ver_check("gcc", "--version", "5.4");
|
let pre_text = element.text().collect::<Vec<_>>().join("\n");
|
||||||
ok &= ver_check("make", "--version", "4.0");
|
|
||||||
ok &= ver_check("tar", "--version", "1.22");
|
|
||||||
|
|
||||||
// Kernel check
|
for line in pre_text.lines() {
|
||||||
if let Some(kernel) = run_command("uname", &["-r"]) {
|
let line = line.trim();
|
||||||
if check_version(&kernel, "5.4") {
|
if line.starts_with("ver_check") {
|
||||||
println!("OK: Linux Kernel {} >= 5.4", kernel);
|
let parts: Vec<&str> = line.split_whitespace().collect();
|
||||||
} else {
|
if parts.len() >= 4 {
|
||||||
println!("ERROR: Linux Kernel {} is too old (5.4 required)", kernel);
|
let prog = parts[1];
|
||||||
ok = false;
|
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
|
// Alias-Checks
|
||||||
let cores = num_cpus::get();
|
let alias_check = |cmd: &str, expected: &str| {
|
||||||
println!("OK: {} logical cores available", cores);
|
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