diff options
| -rw-r--r-- | src/cfg/config.rs | 2 | ||||
| -rw-r--r-- | src/main.rs | 87 | ||||
| -rw-r--r-- | src/net/http_package.rs (renamed from src/net/http_packages.rs) | 33 | ||||
| -rw-r--r-- | src/net/i2p_package.rs | 110 | ||||
| -rw-r--r-- | src/net/mod.rs | 2 | ||||
| -rw-r--r-- | src/pkgtoolkit/pkgtools.rs | 69 |
6 files changed, 191 insertions, 112 deletions
diff --git a/src/cfg/config.rs b/src/cfg/config.rs index 88c98ff..de9901f 100644 --- a/src/cfg/config.rs +++ b/src/cfg/config.rs @@ -125,6 +125,6 @@ impl Config { */ }, }; - Ok(toml::to_string(&generator)?) + toml::to_string(&generator) } } diff --git a/src/main.rs b/src/main.rs index 8677fdb..37bd70a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,9 +1,10 @@ mod cfg; -mod net; +mod net; // This should contain both i2p_package and http_package modules mod pkgtoolkit; use crate::cfg::config::Config; -use crate::net::i2p_package::I2PPackage; +use crate::net::{i2p_package::I2PPackage, + http_package::HTTPPackage}; #[allow(unused_imports)] use crate::pkgtoolkit::pkgtools::Package; @@ -12,7 +13,6 @@ use std::fs::File; use std::fs::create_dir_all; use std::io::Write; use std::path::Path; -use tokio; #[derive(Parser)] struct Cli { @@ -55,19 +55,23 @@ enum Commands { #[command(about = "Remote install arguments")] struct RemoteInstallArgs { #[arg(short = 'b', long = "bin")] + #[arg(help = "Install binary package")] bin: bool, + #[arg(short = 'h', long = "http")] + #[arg(help = "Use non-i2p mirror")] http: bool, + #[arg(short = 'c', long = "clean")] + #[arg(help = "Clean cache before install")] clean: bool, } #[tokio::main] -async fn main() -> Result<(), std::io::Error> { +async fn main() -> Result<(), Box<dyn std::error::Error>> { + // Changed return type to be more general let cli: Cli = Cli::parse(); - // Plug in these functions only until the backend is ready for testing (Aplha versions) - // It is necessary for me to understand the I/O model of the future mesk. match &cli.command { Commands::Validate { path } => { println!("Validating {}", path); @@ -77,13 +81,58 @@ async fn main() -> Result<(), std::io::Error> { println!("Building {}", pkgname); return Ok(()); } - Commands::Install { pkgname, - source, + source: _, args, } => { - println!("Installing {}", pkgname); + let config = Config::parse().unwrap(); + + if args.http { + println!("Installing {} via HTTP", pkgname); + let mut http_client = HTTPPackage::new(config); + match http_client.fetch_index_http().await { + Ok(_) => { + log::info!("Index fetched successfully."); + } + Err(e) => { + log::error!("Failed to fetch index: {}", e); + return Err(e); + } + } + match http_client.fetch_package_http(pkgname).await { + Ok(_) => { + log::info!("Package '{}' installed successfully.", pkgname); + } + Err(e) => { + log::error!("Failed to install package '{}': {}", pkgname, e); + return Err(e); + } + } + } else { + println!("Installing {} via I2P", pkgname); + let mut i2p_client = I2PPackage::new(config); + + match i2p_client.fetch_index().await { + Ok(_) => { + log::info!("Index fetched successfully."); + } + Err(e) => { + log::error!("Failed to fetch index: {}", e); + return Err(e); + } + } + match i2p_client.fetch_package(pkgname).await { + Ok(_) => { + log::info!("Package '{}' installed successfully.", pkgname); + } + Err(e) => { + log::error!("Failed to install package '{}': {}", pkgname, e); + return Err(e); + } + } + } + // --- Integration ends here --- return Ok(()); } Commands::Uninstall { pkgname } => { @@ -112,7 +161,7 @@ async fn main() -> Result<(), std::io::Error> { let path = Path::new("/etc/mesk/mesk.toml"); create_dir_all(path.parent().unwrap())?; let mut file = File::create(path)?; - file.write(config.as_bytes())?; + file.write_all(config.as_bytes())?; println!("Config tool ending work."); } else { let config = Config::generate(repo, cachedir, buildir).unwrap(); @@ -134,13 +183,21 @@ async fn main() -> Result<(), std::io::Error> { Commands::Update => { let config = Config::parse().unwrap(); println!("Updating index from {}", config.repo.repo_url); - let mut i2pd = I2PPackage::new(config); - let _index = I2PPackage::fetch_index(&mut i2pd).await; - println!("Index updated"); + + let mut i2p_client = I2PPackage::new(config); + match i2p_client.fetch_index().await { + Ok(_) => { + println!("Index updated successfully."); + } + Err(e) => { + log::error!("Failed to update index: {}", e); + return Err(e); + } + } return Ok(()); } Commands::Upgrade { pkgname } => { - println!("Upgrading all packages"); + println!("Upgrading {}", pkgname.as_deref().unwrap_or("all packages")); return Ok(()); } Commands::Credits => { @@ -148,7 +205,7 @@ async fn main() -> Result<(), std::io::Error> { "CREATED BY: Asya and Namilsk as part of the Anthrill independent Global network distribution project" ); println!(" "); - println!("The Anthrill project repos: https://codeberg.org/NamelessTeam"); + println!("The Anthrill project repos: https://codeberg.org/NamelessTeam "); } } diff --git a/src/net/http_packages.rs b/src/net/http_package.rs index 05b2ac6..ec7c318 100644 --- a/src/net/http_packages.rs +++ b/src/net/http_package.rs @@ -1,5 +1,6 @@ use crate::cfg::config::Config; use crate::pkgtoolkit::pkgtools::Package; +use futures_util::stream::TryStreamExt; use indicatif::{ProgressBar, ProgressStyle}; use reqwest; use serde::Deserialize; @@ -7,7 +8,6 @@ use std::collections::HashMap; use std::path::Path; use tokio::fs::File; use tokio::io::AsyncWriteExt; -use futures_util::stream::TryStreamExt; pub struct HTTPPackage { config: Config, @@ -72,7 +72,7 @@ impl HTTPPackage { let pb = ProgressBar::new_spinner(); pb.set_style( ProgressStyle::default_spinner() - .template("{spinner:.green} [{elapsed_precise}] Fetching INDEX.tar.gz...")? + .template("{spinner:.green} [{elapsed_precise}] Fetching INDEX.tar.gz...")?, ); pb }; @@ -125,7 +125,10 @@ impl HTTPPackage { } self.index_packages = Some(package_map.clone()); - log::info!("Index loaded successfully, {} packages found.", package_map.len()); + log::info!( + "Index loaded successfully, {} packages found.", + package_map.len() + ); Ok(true) } @@ -167,10 +170,10 @@ impl HTTPPackage { pb } else { let pb = ProgressBar::new_spinner(); - pb.set_style( - ProgressStyle::default_spinner() - .template(&format!("{{spinner:.green}} [{{elapsed_precise}}] {}...", description))? - ); + pb.set_style(ProgressStyle::default_spinner().template(&format!( + "{{spinner:.green}} [{{elapsed_precise}}] {}...", + description + ))?); pb }; @@ -203,7 +206,10 @@ impl HTTPPackage { /// /// Returns an error if the index is not loaded, the package is not found in the index, /// the package URL is invalid, the request fails, or if there's an issue writing the file. - pub async fn fetch_package_http(&self, package_name: &str) -> Result<bool, Box<dyn std::error::Error>> { + pub async fn fetch_package_http( + &self, + package_name: &str, + ) -> Result<bool, Box<dyn std::error::Error>> { let package_info = self.fetch_package_info(package_name)?; let url = &package_info.url; @@ -229,12 +235,17 @@ impl HTTPPackage { /// Fetches a specific package identified by `index` (likely the package name). /// Assumes `fetch_index_http` has been called and `self.index_packages` is populated. - pub fn fetch_package_info(&self, package_name: &str) -> Result<&Package, Box<dyn std::error::Error>> { - let packages = self.index_packages.as_ref() + pub fn fetch_package_info( + &self, + package_name: &str, + ) -> Result<&Package, Box<dyn std::error::Error>> { + let packages = self + .index_packages + .as_ref() .ok_or("Index not loaded. Call fetch_index_http first.")?; let pkg_info = packages .get(package_name) .ok_or(format!("Package '{}' not found in index.", package_name))?; Ok(pkg_info) } -}
\ No newline at end of file +} diff --git a/src/net/i2p_package.rs b/src/net/i2p_package.rs index 335dbea..751d561 100644 --- a/src/net/i2p_package.rs +++ b/src/net/i2p_package.rs @@ -10,9 +10,9 @@ use emissary_core::runtime::{ }; */ +use indicatif::{ProgressBar, ProgressStyle}; use std::{collections::HashMap, path::Path}; use tokio::io::{AsyncReadExt, AsyncWriteExt, BufReader}; -use indicatif::{ProgressBar, ProgressStyle}; use url; use yosemite::Session; @@ -20,12 +20,12 @@ use yosemite::SessionOptions; pub struct I2PPackage { config: Config, - index_packages: Option<HashMap<String, Package>>, + index_packages: Option<HashMap<String, Package>>, } #[derive(Deserialize, Debug)] struct IndexData { - packages: Vec<Package>, + packages: Vec<Package>, } impl I2PPackage { @@ -34,10 +34,10 @@ impl I2PPackage { /// # Returns /// /// A new I2P object with the given configuration. The session is initially set to None and the connected status is set to false. - pub fn new(config: Config) -> Self { - I2PPackage { - config: config, - index_packages: None + pub fn new(cfg: Config) -> Self { + I2PPackage { + config: cfg, + index_packages: None, } } @@ -47,7 +47,7 @@ impl I2PPackage { /// # Errors /// /// Returns an error if the request fails, if the response status is not successful, or if there's an issue while reading or writing the file. - pub async fn fetch_index(&mut self) -> Result<bool, Box<dyn std::error::Error>> { + pub async fn fetch_index(&mut self) -> Result<bool, Box<dyn std::error::Error>> { let repo_url_str = &self.config.repo.repo_url; let cache_dir = &self.config.paths.cache_dir; @@ -85,7 +85,8 @@ impl I2PPackage { return Err(format!( "HTTP Error: {}", headers_str.lines().next().unwrap_or("Unknown") - ).into()); + ) + .into()); } let content_length = headers_str @@ -93,12 +94,11 @@ impl I2PPackage { .find(|line| line.to_lowercase().starts_with("content-length:")) .and_then(|line| line.split_at(15).1.trim().parse::<u64>().ok()) .unwrap_or(0); - + let body_start = headers_end + 4; let mut body_reader = std::io::Cursor::new(&response_buffer[body_start..]); let file_path = Path::new(cache_dir).join("INDEX.tar.gz"); - let pb = if content_length > 0 { let pb = ProgressBar::new(content_length); pb.set_style(ProgressStyle::default_bar() @@ -107,8 +107,9 @@ impl I2PPackage { pb } else { let pb = ProgressBar::new_spinner(); - pb.set_style(ProgressStyle::default_spinner() - .template("{spinner:.green} [{elapsed_precise}] Fetching INDEX.tar.gz...")? + pb.set_style( + ProgressStyle::default_spinner() + .template("{spinner:.green} [{elapsed_precise}] Fetching INDEX.tar.gz...")?, ); pb }; @@ -119,11 +120,10 @@ impl I2PPackage { let mut buffer = vec![0; chunk_size as usize]; loop { - let bytes_read_result = std::io::Read::read(&mut body_reader, &mut buffer); let bytes_read = match bytes_read_result { - Ok(n) => n, - Err(e) => return Err(e.into()), + Ok(n) => n, + Err(e) => return Err(e.into()), }; if bytes_read == 0 { @@ -141,13 +141,13 @@ impl I2PPackage { pb.finish_with_message("INDEX.tar.gz download finished"); log::info!("Extracting INDEX.tar.gz to cache directory..."); - Package::extract_archive(&file_path.to_string_lossy())?; + Package::extract_archive(&file_path.to_string_lossy())?; - let index_toml_path = Path::new(cache_dir).join("INDEX.toml"); + let index_toml_path = Path::new(cache_dir).join("INDEX.toml"); if !index_toml_path.exists() { - log::warn!("INDEX.toml not found in INDEX.tar.gz. Proceeding without index data."); - self.index_packages = Some(HashMap::new()); - return Ok(true); + log::warn!("INDEX.toml not found in INDEX.tar.gz. Proceeding without index data."); + self.index_packages = Some(HashMap::new()); + return Ok(true); } let index_content = std::fs::read_to_string(&index_toml_path)?; @@ -155,23 +155,25 @@ impl I2PPackage { let mut package_map = HashMap::new(); for pkg in index_data.packages { - // PKG_URL = /repo/package.mesk - // FULL URL = "http://mesk.anthrill.i2p/i2p/repo/pkg.mesk" - let base_url = url::Url::parse(&self.config.repo.repo_url)?; - let full_url = base_url.join(&pkg.url)?; - let mut pkg_clone = pkg.clone(); - pkg_clone.url = full_url.to_string(); - - package_map.insert(pkg_clone.name.clone(), pkg_clone); + // PKG_URL = /repo/package.mesk + // FULL URL = "http://mesk.anthrill.i2p/i2p/repo/pkg.mesk" + let base_url = url::Url::parse(&self.config.repo.repo_url)?; + let full_url = base_url.join(&pkg.url)?; + let mut pkg_clone = pkg.clone(); + pkg_clone.url = full_url.to_string(); + + package_map.insert(pkg_clone.name.clone(), pkg_clone); } self.index_packages = Some(package_map.clone()); - log::info!("Index loaded successfully, {} packages found.", package_map.len()); + log::info!( + "Index loaded successfully, {} packages found.", + package_map.len() + ); Ok(true) } - /// An internal auxiliary function for downloading data and writing it to a file with a progress display. /// /// # Arguments @@ -198,9 +200,10 @@ impl I2PPackage { pb } else { let pb = ProgressBar::new_spinner(); - pb.set_style(ProgressStyle::default_spinner() - .template(&format!("{{spinner:.green}} [{{elapsed_precise}}] {}...", description))? - ); + pb.set_style(ProgressStyle::default_spinner().template(&format!( + "{{spinner:.green}} [{{elapsed_precise}}] {}...", + description + ))?); pb }; @@ -223,10 +226,16 @@ impl I2PPackage { /// Fetches a specific package identified by `index` (likely the package name). /// Assumes `fetch_index` has been called and `self.index_packages` is populated. - pub fn fetch_package_info(&self, package_name: &str) -> Result<&Package, Box<dyn std::error::Error>> { - let packages = self.index_packages.as_ref() + pub fn fetch_package_info( + &self, + package_name: &str, + ) -> Result<&Package, Box<dyn std::error::Error>> { + let packages = self + .index_packages + .as_ref() .ok_or("Index not loaded. Call fetch_index first.")?; - let pkg_info = packages.get(package_name) + let pkg_info = packages + .get(package_name) .ok_or(format!("Package '{}' not found in index.", package_name))?; Ok(pkg_info) } @@ -239,9 +248,12 @@ impl I2PPackage { /// /// Returns an error if the index is not loaded, the package is not found in the index, /// the package URL is invalid, the request fails, or if there's an issue writing the file. - /// Why didn't I just abstract the download functionality into a separate function initially? + /// Why didn't I just abstract the download functionality into a separate function initially? /// Yes, I'm scared to work with fetch_index, even I don't often write such shit code. - pub async fn fetch_package(&self, package_name: &str) -> Result<bool, Box<dyn std::error::Error>> { + pub async fn fetch_package( + &self, + package_name: &str, + ) -> Result<bool, Box<dyn std::error::Error>> { let package_info = self.fetch_package_info(package_name)?; let url = url::Url::parse(&package_info.url)?; let host = url.host_str().ok_or("No host in package URL")?; @@ -271,7 +283,8 @@ impl I2PPackage { return Err(format!( "HTTP Error: {}", headers_str.lines().next().unwrap_or("Unknown") - ).into()); + ) + .into()); } let content_length = headers_str @@ -291,10 +304,19 @@ impl I2PPackage { let cache_dir = &self.config.paths.cache_dir; let file_path = Path::new(cache_dir).join(file_name); - Self::download_and_write_file_with_progress(body_bytes, &file_path, content_length, file_name).await?; - - log::info!("Package '{}' downloaded successfully to {:?}", package_name, file_path); + Self::download_and_write_file_with_progress( + body_bytes, + &file_path, + content_length, + file_name, + ) + .await?; + + log::info!( + "Package '{}' downloaded successfully to {:?}", + package_name, + file_path + ); Ok(true) } } - diff --git a/src/net/mod.rs b/src/net/mod.rs index 5a5aca0..1a04189 100644 --- a/src/net/mod.rs +++ b/src/net/mod.rs @@ -1,2 +1,2 @@ -pub mod http_packages; +pub mod http_package; pub mod i2p_package; diff --git a/src/pkgtoolkit/pkgtools.rs b/src/pkgtoolkit/pkgtools.rs index 8198624..877c8c8 100644 --- a/src/pkgtoolkit/pkgtools.rs +++ b/src/pkgtoolkit/pkgtools.rs @@ -8,7 +8,6 @@ use std::{ path::Path, process::Command, str, - collections::HashMap }; // use emissary_core::i2np::tunnel::build; @@ -38,7 +37,7 @@ pub struct Package { } #[derive(Deserialize, Debug, Clone)] -pub struct Install_meta { +pub struct InstallMeta { path: String, user: String, group: String, @@ -51,7 +50,7 @@ pub struct Install_meta { #[derive(Deserialize, Debug, Clone)] struct Install { package: Package, - install: Install_meta, + install: InstallMeta, } #[allow(dead_code)] @@ -102,9 +101,8 @@ impl Package { /// Returns an error if the build command fails. fn execute_build(&self, build_meta: &Build) -> Result<(), std::io::Error> { let config = Config::parse() - .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e.to_string()))?; - let build_dir = - Path::new(&config.paths.cache_dir).join(format!("{}-{}", self.name, self.version)); + .map_err(|e| std::io::Error::other(e.to_string()))?; + let build_dir = Path::new(&config.paths.cache_dir).join(format!("{}-{}", self.name, self.version)); let mut cmd = match build_meta.build_system { BuildSystems::Make => { @@ -142,16 +140,14 @@ impl Package { }; let output = cmd.output().map_err(|e| { - std::io::Error::new( - std::io::ErrorKind::Other, + std::io::Error::other( format!("Build command failed: {}", e), ) })?; if !output.status.success() { let stderr = String::from_utf8_lossy(&output.stderr); - return Err(std::io::Error::new( - std::io::ErrorKind::Other, + return Err(std::io::Error::other( format!("Build failed:\n{}", stderr), )); } @@ -197,6 +193,7 @@ impl Package { /// /// Returns an error if the `cache_dir` cannot be created, if the required 'INSTALL' file /// is not found, or if the 'INSTALL' file is empty. + #[allow(clippy::type_complexity)] fn loadmeta( minimal_package_meta: &mut Self, ) -> Result<(Install, Option<Setts>, Option<Build>), Box<dyn std::error::Error>> { @@ -388,35 +385,35 @@ impl Package { // BUILD NOT EMPTY. SOURCE: -> BUILD -> INSTALL -> SETTS // BUILD EMPTY. BIN: -> INSTALL -> SETTS enum Strategies { - BIN, - SOURCE, + Bin, + Source, } let strategy; //default if build_meta.is_none() { log::info!("BUILD file is empty. Skipping build, preparing to install"); - strategy = Strategies::BIN; + strategy = Strategies::Bin; } else { - strategy = Strategies::SOURCE; + strategy = Strategies::Source; log::info!("BUILD file is not empty. Skipping install, preparing to build"); } match strategy { - Strategies::BIN => { + Strategies::Bin => { if install_meta.install.custom_script.is_none() { log::info!("Strategy: BIN; No custom script. Running default install hook."); } else { log::info!("Strategy: BIN; Running custom script."); let script = install_meta.install.custom_script.as_ref().unwrap(); if !script.starts_with("./") { - let _output = std::process::Command::new(format!("{}", script)); + let _output = std::process::Command::new(script); } else { let _output = std::process::Command::new(format!("/bin/sh '{}'", script)); } } } - Strategies::SOURCE => { + Strategies::Source => { log::info!("Strategy: SOURCE; Running default build hook."); let _ = self.execute_build(&build_meta.unwrap()); } @@ -437,9 +434,9 @@ impl Package { /// Returns an error if the BUILD file is invalid or if the build or install hook fails. pub fn install(&mut self) -> Result<bool, std::io::Error> { let config = Config::parse() - .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e.to_string()))?; + .map_err(|e| std::io::Error::other( e.to_string()))?; let (install_meta, _setts_meta, build_meta) = Self::loadmeta(self) - .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e.to_string()))?; + .map_err(|e| std::io::Error::other(e.to_string()))?; let is_build_present_and_not_empty = build_meta.is_some(); @@ -460,16 +457,14 @@ impl Package { .current_dir(&build_dir) .output() .map_err(|e| { - std::io::Error::new( - std::io::ErrorKind::Other, + std::io::Error::other( format!("'make install' failed: {}", e), ) })?; if !output.status.success() { let stderr = String::from_utf8_lossy(&output.stderr); - return Err(std::io::Error::new( - std::io::ErrorKind::Other, + return Err(std::io::Error::other( format!("'make install' failed:\n{}", stderr), )); } @@ -490,15 +485,13 @@ impl Package { Command::new(script).status() } .map_err(|e| { - std::io::Error::new( - std::io::ErrorKind::Other, + std::io::Error::other( format!("Failed to run custom script: {}", e), ) })?; if !status.success() { - return Err(std::io::Error::new( - std::io::ErrorKind::Other, + return Err(std::io::Error::other( "Custom install script failed", )); } @@ -507,27 +500,22 @@ impl Package { "No custom script. Running default install hook for {}", install_meta.package.name ); - // --- Дефолтный хук установки --- - // 1. Копируем файл из build_dir (предположим, что бинарный файл лежит в корне распакованного архива) - let source_file_name = &self.name; // Предполагаем имя файла = имя пакета + let source_file_name = &self.name; let build_dir = Path::new(&config.paths.cache_dir) .join(format!("{}-{}", self.name, self.version)); let src_path = build_dir.join(source_file_name); let dest_path = Path::new(&install_meta.install.path); - // Убедимся, что целевая директория существует if let Some(parent) = dest_path.parent() { create_dir_all(parent).map_err(|e| { - std::io::Error::new( - std::io::ErrorKind::Other, + std::io::Error::other( format!("Failed to create parent dir: {}", e), ) })?; } fs::copy(&src_path, dest_path).map_err(|e| { - std::io::Error::new( - std::io::ErrorKind::Other, + std::io::Error::other( format!("Failed to copy file: {}", e), ) })?; @@ -540,19 +528,20 @@ impl Package { })?; let perms = PermissionsExt::from_mode(mode); fs::set_permissions(dest_path, perms).map_err(|e| { - std::io::Error::new( - std::io::ErrorKind::Other, + std::io::Error::other( format!("Failed to set permissions: {}", e), ) })?; let output = Command::new("chown") - .arg(&format!("{}:{}", install_meta.install.user, install_meta.install.group)) + .arg(format!( + "{}:{}", + install_meta.install.user, install_meta.install.group + )) .arg(dest_path) .output() .map_err(|e| { - std::io::Error::new( - std::io::ErrorKind::Other, + std::io::Error::other( format!("'chown' command failed: {}", e), ) })?; |
