mod cfg; mod net; mod openpgp; mod pkgtoolkit; mod router; use crate::cfg::config::Config; use crate::net::{http_package::HTTPPackage, i2p_package::I2PPackage}; use crate::pkgtoolkit::Package; use crate::pkgtoolkit::archive::ArchiveOperations; use crate::pkgtoolkit::build::BuildOperations; use crate::pkgtoolkit::git_source::GitSource; use crate::pkgtoolkit::index::IndexOperations; use crate::pkgtoolkit::install::InstallOperations; #[cfg(feature = "integrated-router")] use crate::router::manager::RouterManager; use clap::{Args, Parser, Subcommand}; use std::io::Write; use std::path::Path; #[derive(Parser)] struct Cli { #[command(subcommand)] command: Commands, } #[derive(Subcommand)] enum Commands { #[command(about = "Validate .mesk package archive")] Validate { path: String }, #[command(about = "Update all repositories index")] Update, #[command(about = "Upgrade all packages or a specific package")] Upgrade { pkgname: Option }, #[command(about = "Build package from .mesk ")] Build { pkgname: String }, #[command(about = "Install package")] Install { #[arg(help = "Package name or path to local .mesk file")] pkgname: String, source: Option, #[arg(short = 'p', long = "path")] #[arg(help = "Install from local .mesk file")] path: bool, #[command(flatten)] args: RemoteInstallArgs, }, #[command(about = "Uninstall package")] Uninstall { pkgname: String }, #[command(about = "Get package source")] GetSource { pkgname: String }, #[command(about = "Generator of config file")] DefaultConfig { repo: Option, cachedir: Option, buildir: Option, installed_db: Option, }, #[command(about = "Maintaners, links, developers and more info")] Credits, #[command(about = "Generate index for repository path")] GenIndex { path: String }, } #[derive(Args, Clone)] #[command(about = "Remote install arguments")] struct RemoteInstallArgs { #[arg(short = 'b', long = "bin")] #[arg(help = "Install binary package")] bin: bool, #[arg(short = 't', long = "http")] #[arg(help = "Use non-i2p mirror")] http: bool, #[arg(short = 'c', long = "clean")] #[arg(help = "Clean cache before install")] clean: bool, } /// Helper to initialize router manager when integrated-router feature is enabled #[cfg(feature = "integrated-router")] async fn init_router_manager( config: &Config, ) -> Result, Box> { if !config.router.integrated_router { return Ok(None); } println!("Starting integrated router..."); match RouterManager::new(config).await { Ok(manager) => { println!("Router manager initialized"); Ok(Some(manager)) } Err(e) => { eprintln!("Failed to initialize router manager: {}", e); Err(e) } } } /// Helper to check if router is running (integrated-router feature only) #[cfg(feature = "integrated-router")] async fn ensure_router_ready( manager: &Option, ) -> Result<(), Box> { if let Some(m) = manager { if !m.is_running() { eprintln!("Router is not running, cannot proceed with I2P operation"); return Err(std::io::Error::other("Router not running").into()); } } Ok(()) } #[tokio::main] async fn main() -> Result<(), Box> { let cli: Cli = Cli::parse(); match &cli.command { Commands::Validate { path } => { println!("Validating {}", path); match Package::check(path.to_string()) { Ok(is_valid) => { if is_valid { println!("Package archive is valid."); } else { println!("Package archive is invalid."); return Err(std::io::Error::other("Invalid package archive").into()); } } Err(e) => { eprintln!("Failed to validate package '{}': {}", path, e); return Err(e.into()); } } Ok(()) } Commands::Build { pkgname } => { println!("Building package from archive: {}", pkgname); let config = Config::parse()?; let path = Path::new(&pkgname); if !path.exists() { return Err(std::io::Error::other(format!( "Package archive not found: {}", pkgname )) .into()); } if !path.is_file() { return Err( std::io::Error::other(format!("Path is not a file: {}", pkgname)).into(), ); } println!("Extracting archive..."); Package::extract_archive(pkgname)?; let cache_dir = &config.paths.cache_dir; let mut pkg_dir_name = None; for entry in std::fs::read_dir(cache_dir)? { let entry = entry?; let path = entry.path(); if path.is_dir() { let dir_name = path.file_name().unwrap().to_string_lossy(); if dir_name.contains('-') && dir_name != "temp_extract" { let install_path = path.join("INSTALL"); if install_path.exists() { pkg_dir_name = Some(dir_name.to_string()); break; } } } } let pkg_dir_name = pkg_dir_name .ok_or_else(|| std::io::Error::other("Package directory not found in cache"))?; let install_toml_path = Path::new(cache_dir).join(format!("{}/INSTALL", pkg_dir_name)); if !install_toml_path.exists() { return Err( std::io::Error::other("INSTALL file not found in package directory").into(), ); } let install_content = std::fs::read_to_string(&install_toml_path)?; let install_data: crate::pkgtoolkit::types::Install = toml::from_str(&install_content)?; let mut pkg = install_data.package; println!("Building package '{}'...", pkg.name); pkg.build()?; println!("Package '{}' built successfully.", pkg.name); Ok(()) } Commands::Install { pkgname, source: _, path, args, } => { let config = Config::parse()?; #[cfg(feature = "integrated-router")] let router_manager = init_router_manager(&config).await?; if *path { println!("Installing package from local file: {}", pkgname); println!("Extracting archive..."); Package::extract_archive(pkgname)?; let cache_dir = &config.paths.cache_dir; let mut pkg_dir_name = None; for entry in std::fs::read_dir(cache_dir)? { let entry = entry?; let path = entry.path(); if path.is_dir() { let dir_name = path.file_name().unwrap().to_string_lossy(); if dir_name.contains('-') && dir_name != "temp_extract" { let install_path = path.join("INSTALL"); if install_path.exists() { pkg_dir_name = Some(dir_name.to_string()); break; } } } } let pkg_dir_name = pkg_dir_name .ok_or_else(|| std::io::Error::other("Package directory not found in cache"))?; let install_toml_path = Path::new(cache_dir).join(format!("{}/INSTALL", pkg_dir_name)); let install_content = std::fs::read_to_string(&install_toml_path)?; let install_data: crate::pkgtoolkit::types::Install = toml::from_str(&install_content)?; let mut pkg = install_data.package; println!("Building package '{}'...", pkg.name); pkg.build()?; println!("Installing package '{}'...", pkg.name); pkg.install()?; println!("Package '{}' installed successfully.", pkg.name); } else { #[cfg(feature = "integrated-router")] { if !args.http && config.router.integrated_router { println!("Waiting for router to be ready..."); tokio::time::sleep(std::time::Duration::from_secs(3)).await; ensure_router_ready(&router_manager).await?; } } if args.http { println!("Installing {} via non-i2p mirror", pkgname); let mut http_client = HTTPPackage::new(config); http_client.fetch_index_http().await?; println!("Index fetched successfully."); http_client.fetch_package_http(pkgname).await?; println!("Package '{}' installed successfully.", pkgname); } else { println!("Installing {} via I2P", pkgname); let mut i2p_client = I2PPackage::new(config); i2p_client.fetch_index().await?; println!("Index fetched successfully."); i2p_client.fetch_package(pkgname).await?; println!("Package '{}' installed successfully.", pkgname); } } Ok(()) } Commands::Uninstall { pkgname } => { println!("Uninstalling package: {}", pkgname); let installed_packages = Package::list_installed_packages().map_err(|e| { eprintln!("Failed to list installed packages: {}", e); e })?; let package_manifest = installed_packages .iter() .find(|p| p.name == *pkgname) .ok_or_else(|| { std::io::Error::other(format!("Package '{}' is not installed", pkgname)) })?; let package = Package { name: package_manifest.name.clone(), version: package_manifest.version.clone(), ..Default::default() }; match package.uninstall() { Ok(true) => { println!("Successfully uninstalled package: {}", pkgname); Ok(()) } Ok(false) => { eprintln!( "Some files could not be removed during uninstallation of {}", pkgname ); Err(std::io::Error::other("Partial uninstallation occurred").into()) } Err(e) => { eprintln!("Failed to uninstall package {}: {}", pkgname, e); Err(e.into()) } } } Commands::GetSource { pkgname } => { let config = Config::parse()?; #[cfg(feature = "integrated-router")] { let router_manager = init_router_manager(&config).await?; if config.router.integrated_router { println!("Waiting for router to be ready..."); tokio::time::sleep(std::time::Duration::from_secs(2)).await; ensure_router_ready(&router_manager).await?; } let _source_path = GitSource::get_source_by_name(pkgname, &config)?; } #[cfg(not(feature = "integrated-router"))] { let _package = I2PPackage::new(config); let _package_fetch = _package.fetch_package(pkgname); } println!("Getting source of {}", pkgname); println!("Source code successfully downloaded to cachedir"); Ok(()) } Commands::DefaultConfig { repo, cachedir, buildir, installed_db, } => { println!("Generating config file"); if cachedir.is_none() && repo.is_none() && buildir.is_none() { let config = Config::default()?; println!("---- Start of generated config ----"); println!("{}", config); println!("---- End of generated config ----"); eprintln!("Writing the default config to /etc/mesk/mesk.toml"); let path = Path::new("/etc/mesk/mesk.toml"); std::fs::create_dir_all(path.parent().unwrap())?; let mut file = std::fs::File::create(path)?; file.write_all(config.as_bytes())?; println!("Config tool ending work."); } else { let config = Config::generate(repo, cachedir, buildir, installed_db).unwrap(); println!("---- Start of generated config ----"); println!("{:?}", config); println!("---- End of generated config ----"); eprintln!("Writing the default config to /etc/mesk/mesk.toml"); let path = Path::new("/etc/mesk/mesk.toml"); std::fs::create_dir_all(path.parent().unwrap())?; let mut file = std::fs::File::create(path)?; file.write_all(config.as_bytes())?; println!("Config tool ending work."); } Ok(()) } Commands::Update => { let config = Config::parse()?; #[cfg(feature = "integrated-router")] { let router_manager = init_router_manager(&config).await?; if config.router.integrated_router { println!("Waiting for router to be ready..."); tokio::time::sleep(std::time::Duration::from_secs(2)).await; ensure_router_ready(&router_manager).await?; } } println!("Updating index from {}", config.repo.repo_url); let mut i2p_client = I2PPackage::new(config); i2p_client.fetch_index().await?; println!("Index updated successfully."); Ok(()) } Commands::Upgrade { pkgname } => { println!("Upgrading {}", pkgname.as_deref().unwrap_or("all packages")); Ok(()) } Commands::Credits => { println!( "CREATED BY: Namilsk as part of the Anthrill independent Global network distribution project" ); println!(" "); println!("The Anthrill project repos: https://codeberg.org/NamelessTeam "); Ok(()) } Commands::GenIndex { path } => { println!("Generating index for {}", path); Package::gen_index(path)?; println!("Index generated successfully."); Ok(()) } } }