init
This commit is contained in:
commit
0d177729ab
15 changed files with 2263 additions and 0 deletions
1
.envrc
Normal file
1
.envrc
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
use flake
|
||||||
13
.github/workflows/build_nix.yml
vendored
Normal file
13
.github/workflows/build_nix.yml
vendored
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
name: "Build legacy Nix package on Ubuntu"
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- uses: cachix/install-nix-action@v26
|
||||||
|
- name: Building package
|
||||||
|
run: nix build
|
||||||
21
.gitignore
vendored
Normal file
21
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
# Generated by Cargo
|
||||||
|
# will have compiled files and executables
|
||||||
|
debug
|
||||||
|
target
|
||||||
|
|
||||||
|
# These are backup files generated by rustfmt
|
||||||
|
**/*.rs.bk
|
||||||
|
|
||||||
|
# MSVC Windows builds of rustc generate these, which store debugging information
|
||||||
|
*.pdb
|
||||||
|
|
||||||
|
# Generated by cargo mutants
|
||||||
|
# Contains mutation testing data
|
||||||
|
**/mutants.out*/
|
||||||
|
|
||||||
|
# RustRover
|
||||||
|
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
||||||
|
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
||||||
|
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
||||||
|
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
||||||
|
#.idea/
|
||||||
1703
Cargo.lock
generated
Normal file
1703
Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load diff
13
Cargo.toml
Normal file
13
Cargo.toml
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
[package]
|
||||||
|
name = "package_management"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2024"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
console = "0.16.1"
|
||||||
|
indicatif = "0.18.0"
|
||||||
|
md5 = "0.8.0"
|
||||||
|
rand = "0.9.2"
|
||||||
|
reqwest = { version = "0.12.23", features = ["blocking", "json"] }
|
||||||
|
serde = { version = "1.0.228", features = ["derive"] }
|
||||||
|
|
||||||
7
default.nix
Normal file
7
default.nix
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
(import (
|
||||||
|
fetchTarball {
|
||||||
|
url = "https://github.com/edolstra/flake-compat/archive/99f1c2157fba4bfe6211a321fd0ee43199025dbf.tar.gz";
|
||||||
|
sha256 = "0x2jn3vrawwv9xp15674wjz9pixwjyj3j771izayl962zziivbx2"; }
|
||||||
|
) {
|
||||||
|
src = ./.;
|
||||||
|
}).defaultNix
|
||||||
137
flake.lock
generated
Normal file
137
flake.lock
generated
Normal file
|
|
@ -0,0 +1,137 @@
|
||||||
|
{
|
||||||
|
"nodes": {
|
||||||
|
"fenix": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs": [
|
||||||
|
"naersk",
|
||||||
|
"nixpkgs"
|
||||||
|
],
|
||||||
|
"rust-analyzer-src": "rust-analyzer-src"
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1752475459,
|
||||||
|
"narHash": "sha256-z6QEu4ZFuHiqdOPbYss4/Q8B0BFhacR8ts6jO/F/aOU=",
|
||||||
|
"owner": "nix-community",
|
||||||
|
"repo": "fenix",
|
||||||
|
"rev": "bf0d6f70f4c9a9cf8845f992105652173f4b617f",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nix-community",
|
||||||
|
"repo": "fenix",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"naersk": {
|
||||||
|
"inputs": {
|
||||||
|
"fenix": "fenix",
|
||||||
|
"nixpkgs": "nixpkgs"
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1752689277,
|
||||||
|
"narHash": "sha256-uldUBFkZe/E7qbvxa3mH1ItrWZyT6w1dBKJQF/3ZSsc=",
|
||||||
|
"owner": "nix-community",
|
||||||
|
"repo": "naersk",
|
||||||
|
"rev": "0e72363d0938b0208d6c646d10649164c43f4d64",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nix-community",
|
||||||
|
"ref": "master",
|
||||||
|
"repo": "naersk",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nixpkgs": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1752077645,
|
||||||
|
"narHash": "sha256-HM791ZQtXV93xtCY+ZxG1REzhQenSQO020cu6rHtAPk=",
|
||||||
|
"owner": "NixOS",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "be9e214982e20b8310878ac2baa063a961c1bdf6",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "NixOS",
|
||||||
|
"ref": "nixpkgs-unstable",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nixpkgs_2": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1759070547,
|
||||||
|
"narHash": "sha256-JVZl8NaVRYb0+381nl7LvPE+A774/dRpif01FKLrYFQ=",
|
||||||
|
"owner": "NixOS",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "647e5c14cbd5067f44ac86b74f014962df460840",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "NixOS",
|
||||||
|
"ref": "nixpkgs-unstable",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": {
|
||||||
|
"inputs": {
|
||||||
|
"naersk": "naersk",
|
||||||
|
"nixpkgs": "nixpkgs_2",
|
||||||
|
"utils": "utils"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"rust-analyzer-src": {
|
||||||
|
"flake": false,
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1752428706,
|
||||||
|
"narHash": "sha256-EJcdxw3aXfP8Ex1Nm3s0awyH9egQvB2Gu+QEnJn2Sfg=",
|
||||||
|
"owner": "rust-lang",
|
||||||
|
"repo": "rust-analyzer",
|
||||||
|
"rev": "591e3b7624be97e4443ea7b5542c191311aa141d",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "rust-lang",
|
||||||
|
"ref": "nightly",
|
||||||
|
"repo": "rust-analyzer",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"systems": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1681028828,
|
||||||
|
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"utils": {
|
||||||
|
"inputs": {
|
||||||
|
"systems": "systems"
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1731533236,
|
||||||
|
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": "root",
|
||||||
|
"version": 7
|
||||||
|
}
|
||||||
40
flake.nix
Normal file
40
flake.nix
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
{
|
||||||
|
inputs = {
|
||||||
|
naersk.url = "github:nix-community/naersk/master";
|
||||||
|
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
|
||||||
|
utils.url = "github:numtide/flake-utils";
|
||||||
|
};
|
||||||
|
|
||||||
|
outputs =
|
||||||
|
{
|
||||||
|
self,
|
||||||
|
nixpkgs,
|
||||||
|
utils,
|
||||||
|
naersk,
|
||||||
|
}:
|
||||||
|
utils.lib.eachDefaultSystem (
|
||||||
|
system:
|
||||||
|
let
|
||||||
|
pkgs = import nixpkgs { inherit system; };
|
||||||
|
naersk-lib = pkgs.callPackage naersk { };
|
||||||
|
in
|
||||||
|
{
|
||||||
|
defaultPackage = naersk-lib.buildPackage ./.;
|
||||||
|
devShell =
|
||||||
|
with pkgs;
|
||||||
|
mkShell {
|
||||||
|
buildInputs = [
|
||||||
|
cargo
|
||||||
|
rustc
|
||||||
|
rustfmt
|
||||||
|
pre-commit
|
||||||
|
rustPackages.clippy
|
||||||
|
pkg-config
|
||||||
|
openssl
|
||||||
|
gemini-cli-bin
|
||||||
|
];
|
||||||
|
RUST_SRC_PATH = rustPlatform.rustLibSrc;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
7
shell.nix
Normal file
7
shell.nix
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
(import (
|
||||||
|
fetchTarball {
|
||||||
|
url = "https://github.com/edolstra/flake-compat/archive/99f1c2157fba4bfe6211a321fd0ee43199025dbf.tar.gz";
|
||||||
|
sha256 = "0x2jn3vrawwv9xp15674wjz9pixwjyj3j771izayl962zziivbx2"; }
|
||||||
|
) {
|
||||||
|
src = ./.;
|
||||||
|
}).shellNix
|
||||||
137
src/downloader.rs
Normal file
137
src/downloader.rs
Normal file
|
|
@ -0,0 +1,137 @@
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::fs::{self, File};
|
||||||
|
use std::io::{Read, Write};
|
||||||
|
use std::path::Path;
|
||||||
|
use std::sync::Arc;
|
||||||
|
use std::thread;
|
||||||
|
|
||||||
|
use console::style;
|
||||||
|
use indicatif::{MultiProgress, ProgressBar, ProgressStyle};
|
||||||
|
use md5;
|
||||||
|
use reqwest::blocking::Client;
|
||||||
|
|
||||||
|
/// Prüft Datei gegen erwarteten MD5-Hash
|
||||||
|
fn verify_md5(file_path: &Path, expected_hash: &str) -> bool {
|
||||||
|
let mut f = match File::open(file_path) {
|
||||||
|
Ok(f) => f,
|
||||||
|
Err(_) => return false,
|
||||||
|
};
|
||||||
|
let mut buffer = Vec::new();
|
||||||
|
if f.read_to_end(&mut buffer).is_err() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
let digest = md5::compute(&buffer);
|
||||||
|
let hex = format!("{:x}", digest);
|
||||||
|
hex == expected_hash
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Download + Live-MD5-Prüfung
|
||||||
|
pub fn download_files(
|
||||||
|
wget_list: &str,
|
||||||
|
target_dir: &Path,
|
||||||
|
package_mirror: Option<String>,
|
||||||
|
md5_map: Option<&HashMap<String, String>>,
|
||||||
|
) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
fs::create_dir_all(target_dir)?;
|
||||||
|
|
||||||
|
let urls: Vec<&str> = wget_list.lines().filter(|l| !l.trim().is_empty()).collect();
|
||||||
|
let total = urls.len();
|
||||||
|
let client = Arc::new(Client::new());
|
||||||
|
let mp = Arc::new(MultiProgress::new());
|
||||||
|
|
||||||
|
// Clone md5_map before the loop so we can move it into threads
|
||||||
|
let md5_map = md5_map.cloned();
|
||||||
|
|
||||||
|
let mut handles = vec![];
|
||||||
|
|
||||||
|
for (i, url) in urls.into_iter().enumerate() {
|
||||||
|
let client = Arc::clone(&client);
|
||||||
|
let mp = Arc::clone(&mp);
|
||||||
|
let target_dir = target_dir.to_path_buf();
|
||||||
|
let package_mirror = package_mirror.clone();
|
||||||
|
let url = url.to_string();
|
||||||
|
let md5_map = md5_map.clone();
|
||||||
|
|
||||||
|
let handle = thread::spawn(move || -> Result<(), Box<dyn std::error::Error + Send>> {
|
||||||
|
let filename = url.split('/').last().unwrap_or("file.tar.xz");
|
||||||
|
let filepath = target_dir.join(filename);
|
||||||
|
|
||||||
|
let download_url = if let Some(ref mirror) = package_mirror {
|
||||||
|
if url.contains("ftp.gnu.org") {
|
||||||
|
url.replacen("ftp.gnu.org", mirror, 1)
|
||||||
|
} else {
|
||||||
|
url.to_string()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
url.to_string()
|
||||||
|
};
|
||||||
|
|
||||||
|
let pb = mp.add(ProgressBar::new(0));
|
||||||
|
pb.set_style(
|
||||||
|
ProgressStyle::with_template(
|
||||||
|
"{bar:40.cyan/blue} {bytes}/{total_bytes} ({eta}) {msg}",
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
|
.progress_chars("=> "),
|
||||||
|
);
|
||||||
|
pb.set_message(format!(
|
||||||
|
"[{}/{}] {}",
|
||||||
|
i + 1,
|
||||||
|
total,
|
||||||
|
style(filename).yellow()
|
||||||
|
));
|
||||||
|
|
||||||
|
let mut resp = client
|
||||||
|
.get(&download_url)
|
||||||
|
.send()
|
||||||
|
.map_err(|e| Box::new(e) as Box<dyn std::error::Error + Send>)?;
|
||||||
|
let total_size = resp.content_length().unwrap_or(0);
|
||||||
|
pb.set_length(total_size);
|
||||||
|
|
||||||
|
let mut file = File::create(&filepath)
|
||||||
|
.map_err(|e| Box::new(e) as Box<dyn std::error::Error + Send>)?;
|
||||||
|
let mut downloaded: u64 = 0;
|
||||||
|
let mut buffer = [0u8; 8192];
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let bytes_read = resp
|
||||||
|
.read(&mut buffer)
|
||||||
|
.map_err(|e| Box::new(e) as Box<dyn std::error::Error + Send>)?;
|
||||||
|
if bytes_read == 0 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
file.write_all(&buffer[..bytes_read])
|
||||||
|
.map_err(|e| Box::new(e) as Box<dyn std::error::Error + Send>)?;
|
||||||
|
downloaded += bytes_read as u64;
|
||||||
|
pb.set_position(downloaded);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Live-MD5-Prüfung
|
||||||
|
let status = if let Some(ref md5_map) = md5_map {
|
||||||
|
if let Some(expected_hash) = md5_map.get(filename) {
|
||||||
|
if verify_md5(&filepath, expected_hash) {
|
||||||
|
style("✅").green()
|
||||||
|
} else {
|
||||||
|
style("❌").red()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
style("⚠️").yellow()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
style("⚠️").yellow()
|
||||||
|
};
|
||||||
|
|
||||||
|
pb.finish_with_message(format!("{} {}", status, style(filename).yellow()));
|
||||||
|
Ok(())
|
||||||
|
});
|
||||||
|
|
||||||
|
handles.push(handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
for handle in handles {
|
||||||
|
let result = handle.join().unwrap();
|
||||||
|
result.map_err(|e| e as Box<dyn std::error::Error>)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
50
src/main.rs
Normal file
50
src/main.rs
Normal file
|
|
@ -0,0 +1,50 @@
|
||||||
|
mod downloader;
|
||||||
|
mod md5_utils;
|
||||||
|
mod mirrors;
|
||||||
|
mod wget_list;
|
||||||
|
|
||||||
|
use console::style;
|
||||||
|
use rand::Rng;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::env;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
// LFS sources Pfad
|
||||||
|
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()
|
||||||
|
);
|
||||||
|
PathBuf::from(tmp_path).join("sources")
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Mirror für Pakete auswählen
|
||||||
|
let package_mirror = mirrors::choose_package_mirror();
|
||||||
|
|
||||||
|
// Wget-Liste vom Original LFS-Mirror holen
|
||||||
|
let wget_list = wget_list::get_wget_list()?;
|
||||||
|
|
||||||
|
// MD5 Map vorbereiten
|
||||||
|
let mut md5_map: HashMap<String, String> = 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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pakete herunterladen + Live-MD5 prüfen
|
||||||
|
downloader::download_files(&wget_list, &lfs_sources, package_mirror, Some(&md5_map))?;
|
||||||
|
|
||||||
|
println!("{} All done!", style("🎉").green().bold());
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
59
src/md5_utils.rs
Normal file
59
src/md5_utils.rs
Normal file
|
|
@ -0,0 +1,59 @@
|
||||||
|
use console::style;
|
||||||
|
use md5;
|
||||||
|
use std::fs::File;
|
||||||
|
use std::io::{BufRead, BufReader, Read};
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
|
pub fn get_md5sums() -> Result<String, Box<dyn std::error::Error>> {
|
||||||
|
let client = reqwest::blocking::Client::builder()
|
||||||
|
.redirect(reqwest::redirect::Policy::none())
|
||||||
|
.build()?;
|
||||||
|
let res = client
|
||||||
|
.get("https://www.linuxfromscratch.org/~thomas/multilib-m32/md5sums")
|
||||||
|
.send()?
|
||||||
|
.text()?;
|
||||||
|
Ok(res)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn save_md5sums(content: &str, path: &Path) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
std::fs::write(path, content)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn verify_md5sums(
|
||||||
|
md5_file: &Path,
|
||||||
|
sources_dir: &Path,
|
||||||
|
) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
let file = File::open(md5_file)?;
|
||||||
|
for line in BufReader::new(file).lines() {
|
||||||
|
let line = line?;
|
||||||
|
if line.trim().is_empty() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut parts = line.split_whitespace();
|
||||||
|
let expected_hash = parts.next().ok_or("Malformed md5sums line")?;
|
||||||
|
let filename = parts.next().ok_or("Malformed md5sums line")?;
|
||||||
|
let file_path = sources_dir.join(filename);
|
||||||
|
|
||||||
|
let mut f = File::open(&file_path)?;
|
||||||
|
let mut buffer = Vec::new();
|
||||||
|
f.read_to_end(&mut buffer)?;
|
||||||
|
|
||||||
|
let digest = md5::compute(&buffer);
|
||||||
|
let hex = format!("{:x}", digest);
|
||||||
|
|
||||||
|
if hex == expected_hash {
|
||||||
|
println!("{} {} OK", style("✅").green(), filename);
|
||||||
|
} else {
|
||||||
|
println!(
|
||||||
|
"{} {} FAILED (expected {}, got {})",
|
||||||
|
style("❌").red(),
|
||||||
|
filename,
|
||||||
|
expected_hash,
|
||||||
|
hex
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
33
src/mirrors
Normal file
33
src/mirrors
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
use console::Style;
|
||||||
|
use std::io::{self, Write};
|
||||||
|
|
||||||
|
pub fn choose_package_mirror() -> Option<String> {
|
||||||
|
let mirrors = vec![
|
||||||
|
"https://ftp.fau.de",
|
||||||
|
"https://mirror.kernel.org/linux",
|
||||||
|
"https://mirror.example.org/linux",
|
||||||
|
];
|
||||||
|
|
||||||
|
println!("Optional: choose a mirror for source packages:");
|
||||||
|
|
||||||
|
for (i, mirror) in mirrors.iter().enumerate() {
|
||||||
|
println!(" [{}] {}", i + 1, mirror);
|
||||||
|
}
|
||||||
|
|
||||||
|
print!("Enter number or press Enter for default: ");
|
||||||
|
io::stdout().flush().unwrap();
|
||||||
|
|
||||||
|
let mut input = String::new();
|
||||||
|
io::stdin().read_line(&mut input).unwrap();
|
||||||
|
let input = input.trim();
|
||||||
|
|
||||||
|
if input.is_empty() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
let choice = input.parse::<usize>().unwrap_or(1);
|
||||||
|
let chosen = mirrors.get(choice.saturating_sub(1)).unwrap_or(&mirrors[0]);
|
||||||
|
println!("Using package mirror: {}", Style::new().green().apply_to(chosen));
|
||||||
|
Some(chosen.to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
31
src/mirrors.rs
Normal file
31
src/mirrors.rs
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
use console::Style;
|
||||||
|
use std::io::{self, Write};
|
||||||
|
|
||||||
|
pub fn choose_package_mirror() -> Option<String> {
|
||||||
|
let mirrors = vec!["ftp.fau.de", "mirror.kernel.org", "mirror.example.org"];
|
||||||
|
|
||||||
|
println!("Optional: choose a mirror for GNU source packages (replace ftp.gnu.org):");
|
||||||
|
|
||||||
|
for (i, mirror) in mirrors.iter().enumerate() {
|
||||||
|
println!(" [{}] {}", i + 1, mirror);
|
||||||
|
}
|
||||||
|
|
||||||
|
print!("Enter number or press Enter for default: ");
|
||||||
|
io::stdout().flush().unwrap();
|
||||||
|
|
||||||
|
let mut input = String::new();
|
||||||
|
io::stdin().read_line(&mut input).unwrap();
|
||||||
|
let input = input.trim();
|
||||||
|
|
||||||
|
if input.is_empty() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
let choice = input.parse::<usize>().unwrap_or(1);
|
||||||
|
let chosen = mirrors.get(choice.saturating_sub(1)).unwrap_or(&mirrors[0]);
|
||||||
|
println!(
|
||||||
|
"Using package mirror: {}",
|
||||||
|
Style::new().green().apply_to(chosen)
|
||||||
|
);
|
||||||
|
Some(chosen.to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
11
src/wget_list.rs
Normal file
11
src/wget_list.rs
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
use reqwest::blocking::Client;
|
||||||
|
use reqwest::redirect::Policy;
|
||||||
|
|
||||||
|
pub fn get_wget_list() -> Result<String, Box<dyn std::error::Error>> {
|
||||||
|
let client = Client::builder().redirect(Policy::none()).build()?;
|
||||||
|
let res = client
|
||||||
|
.get("https://www.linuxfromscratch.org/~thomas/multilib-m32/wget-list-sysv")
|
||||||
|
.send()?
|
||||||
|
.text()?;
|
||||||
|
Ok(res)
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue