diff options
| author | Namilskyy <alive6863@gmail.com> | 2025-11-29 20:48:12 +0300 |
|---|---|---|
| committer | Namilskyy <alive6863@gmail.com> | 2025-11-29 20:48:12 +0300 |
| commit | 5319e24d9f7b43dbfdfd368ea5ac467970061cd2 (patch) | |
| tree | 89eb1cb8f17962fe0f1bb8b97368a62875a60ded | |
| parent | d10ac4cc08d679e7971296b79c6eafadcdbc78de (diff) | |
Minor warnings fixed, implemented codeberg pages action
| -rw-r--r-- | .codeberg/actions.yml | 96 | ||||
| -rw-r--r-- | src/cfg/config.rs | 63 | ||||
| -rw-r--r-- | src/cfg/mod.rs | 2 | ||||
| -rw-r--r-- | src/main.rs | 131 | ||||
| -rw-r--r-- | src/net/http_packages.rs | 49 | ||||
| -rw-r--r-- | src/net/i2p_package.rs | 121 | ||||
| -rw-r--r-- | src/net/mod.rs | 4 | ||||
| -rw-r--r-- | src/pkgtoolkit/mod.rs | 2 | ||||
| -rw-r--r-- | src/pkgtoolkit/pkgtools.rs | 385 |
9 files changed, 526 insertions, 327 deletions
diff --git a/.codeberg/actions.yml b/.codeberg/actions.yml new file mode 100644 index 0000000..85f4d2d --- /dev/null +++ b/.codeberg/actions.yml @@ -0,0 +1,96 @@ + +on: + push: + branches: [ "main" ] + pull_request: + branches: [ "main" ] + +jobs: + + test-and-check: + runs-on: ubuntu-latest + strategy: + matrix: + rust: [ "stable", "beta" ] + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Install Rust (${{ matrix.rust }}) + uses: dtolnay/rust-toolchain@stable + with: + toolchain: ${{ matrix.rust }} + components: rustfmt, clippy + + - name: Cache Cargo registry and index + uses: actions/cache@v4 + with: + path: | + ~/.cargo/registry + ~/.cargo/git + target + key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} + + - name: Check formatting + run: cargo fmt --all -- --check + + # - name: Lint with clippy + # run: cargo clippy -- -D warnings + + - name: Build + run: cargo build --verbose + + - name: Run tests + run: cargo test --verbose + + build-cross: + runs-on: ubuntu-latest + strategy: + matrix: + target: [ x86_64-unknown-linux-gnu, aarch64-unknown-linux-gnu ] + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Install Rust stable + uses: dtolnay/rust-toolchain@stable + with: + toolchain: stable + targets: ${{ matrix.target }} + + - name: Cache Cargo registry and index + uses: actions/cache@v4 + with: + path: | + ~/.cargo/registry + ~/.cargo/git + target + key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}-target-${{ matrix.target }} + + - name: Install cross-compilation tools + if: matrix.target == 'aarch64-unknown-linux-gnu' + run: | + sudo apt update + sudo apt install -y gcc-aarch64-linux-gnu + + - name: Set up environment for cross-compilation + run: | + echo "CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER=aarch64-linux-gnu-gcc" >> $GITHUB_ENV + if: matrix.target == 'aarch64-unknown-linux-gnu' + + - name: Build for ${{ matrix.target }} + run: cargo build --target ${{ matrix.target }} --release --verbose + +# release: +# runs-on: ubuntu-latest +# needs: [test-and-check, build-cross] # Запускается только если предыдущие задания прошли успешно +# if: startsWith(github.ref, 'refs/tags/') # Запускается только при создании тега +# steps: +# - name: Checkout code +# uses: actions/checkout@v4 +# - name: Install Rust stable +# uses: dtolnay/rust-toolchain@stable +# with: +# toolchain: stable +# - name: Build release +# run: cargo build --release --verbose diff --git a/src/cfg/config.rs b/src/cfg/config.rs index ffccbad..cf6188e 100644 --- a/src/cfg/config.rs +++ b/src/cfg/config.rs @@ -1,5 +1,5 @@ -use std::fs; use serde::{Deserialize, Serialize}; +use std::fs; use toml; #[derive(Debug, Deserialize, Serialize)] @@ -9,16 +9,15 @@ pub enum Loglevel { Debug, Info, Warn, - Error + Error, } - /// `mesk.toml` configuration fields here #[derive(Deserialize, Debug, Serialize)] pub struct Config { - pub repo: Repo, - pub log: Log, - pub paths: Paths, + pub repo: Repo, + pub log: Log, + pub paths: Paths, } #[derive(Deserialize, Debug, Serialize)] @@ -29,29 +28,28 @@ pub struct Log { pub log_level: Loglevel, } -// Rename needed for editing mesk.toml file fields but dont touch code. +// Rename needed for editing mesk.toml file fields but dont touch code. #[derive(Deserialize, Debug, Serialize)] pub struct Repo { - #[serde(rename = "repo_url")] + #[serde(rename = "repo_url")] pub repo_url: String, - #[serde(rename = "auto_update")] + #[serde(rename = "auto_update")] pub auto_update: bool, #[serde(rename = "destination")] pub destination: (String, String), -// #[serde(rename = "arch")] -// pub arch = arch; + // #[serde(rename = "arch")] + // pub arch = arch; } #[derive(Deserialize, Debug, Serialize)] pub struct Paths { - #[serde(rename = "cache_dir")] + #[serde(rename = "cache_dir")] pub cache_dir: String, - #[serde(rename = "build_dir")] + #[serde(rename = "build_dir")] pub build_dir: String, } impl Config { - /// Parse the /etc/mesk.toml file and return the Config object. /// /// This function reads the /etc/mesk.toml file, parses it and returns the Config object. @@ -61,17 +59,15 @@ impl Config { Ok(result) } - /// Return the default configuration as a toml string. /// /// This function returns the default configuration as a toml string. pub fn default() -> Result<String, toml::ser::Error> { let default: Config = Config { repo: Repo { - repo_url: format!("https://mesk.anthrill.i2p/repo/{}/", - std::env::consts::ARCH), + repo_url: format!("https://mesk.anthrill.i2p/repo/{}/", std::env::consts::ARCH), auto_update: true, - destination: (String::from("mesk"), String::from("mesk")), + destination: (String::from("mesk"), String::from("mesk")), // Its a hurt place, you need to generate destinations by i2pd and paste here (to mesk.toml) // Better to leave it empty or set it to (mesk, mesk), now destination conn not implemented }, @@ -89,25 +85,40 @@ impl Config { Ok(toml_str) } - pub fn generate(repo: &Option<String>, cachedir: &Option<String>, buildir: &Option<String>) -> Result<String, toml::ser::Error> { + pub fn generate( + repo: &Option<String>, + cachedir: &Option<String>, + buildir: &Option<String>, + ) -> Result<String, toml::ser::Error> { let generator: Config = Config { repo: Repo { - repo_url: if repo.is_none() { format!("https://mesk.anthrill.i2p/repo/{}/", std::env::consts::ARCH ) } else { repo.clone().unwrap() }, + repo_url: if repo.is_none() { + format!("https://mesk.anthrill.i2p/repo/{}/", std::env::consts::ARCH) + } else { + repo.clone().unwrap() + }, auto_update: true, - destination: (String::from("mesk"), String::from("mesk")), + destination: (String::from("mesk"), String::from("mesk")), }, log: Log { log_file: String::from("/var/log/mesk.log"), log_level: Loglevel::Info, }, paths: Paths { - cache_dir: if cachedir.is_none() { String::from("/var/cache/mesk") } else { cachedir.clone().unwrap() }, - build_dir: if buildir.is_none() { String::from("/var/cache/mesk/build") } else { buildir.clone().unwrap() }, - - /* + cache_dir: if cachedir.is_none() { + String::from("/var/cache/mesk") + } else { + cachedir.clone().unwrap() + }, + build_dir: if buildir.is_none() { + String::from("/var/cache/mesk/build") + } else { + buildir.clone().unwrap() + }, + /* FIXME: I can leave this parameter, but I think it would be better to make the build path in the /var/cache/mesk/$pkgname-$pkgver/BUILD/ - */ + */ }, }; Ok(toml::to_string(&generator)?) diff --git a/src/cfg/mod.rs b/src/cfg/mod.rs index b1ca24c..ef68c36 100644 --- a/src/cfg/mod.rs +++ b/src/cfg/mod.rs @@ -1 +1 @@ -pub mod config;
\ No newline at end of file +pub mod config; diff --git a/src/main.rs b/src/main.rs index 463bd24..e295722 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,15 +3,15 @@ mod net; mod pkgtoolkit; use crate::cfg::config::Config; +use crate::net::i2p_package::I2PPackage; #[allow(unused_imports)] use crate::pkgtoolkit::pkgtools::Package; -use crate::net::i2p_package::I2PPackage; -use clap::{Args, Command, Parser, Subcommand}; +use clap::{Args, Parser, Subcommand}; +use std::fs::File; +use std::fs::create_dir_all; use std::io::Write; use std::path::Path; -use std::fs::create_dir_all; -use std::fs::File; use tokio; #[derive(Parser)] @@ -23,54 +23,43 @@ struct Cli { #[derive(Subcommand)] enum Commands { #[command(about = "Validate .mesk package archive")] - Validate { - path: String, - }, + Validate { path: String }, #[command(about = "Update all repositories index")] - Update, + Update, #[command(about = "Upgrade all packages or a specific package")] - Upgrade { - pkgname: Option<String>, - }, + Upgrade { pkgname: Option<String> }, #[command(about = "Build package from .mesk ")] - Build{ - pkgname: String, - }, + Build { pkgname: String }, #[command(about = "Install package from remote or local sources")] - Install{ - pkgname: String, + Install { + pkgname: String, source: Option<String>, #[command(flatten)] - args: RemoteInstallArgs - + args: RemoteInstallArgs, }, #[command(about = "Uninstall package")] - Uninstall{ - pkgname: String, - }, + Uninstall { pkgname: String }, #[command(about = "Get package source")] - GetSource{ - pkgname: String - }, + GetSource { pkgname: String }, #[command(about = "Generator of config file")] DefaultConfig { - repo: Option<String>, + repo: Option<String>, cachedir: Option<String>, - buildir: Option<String>, + buildir: Option<String>, }, #[command(about = "Maintaners, links, developers and more info")] - Credits + Credits, } -#[derive(Args, Clone)] +#[derive(Args, Clone)] #[command(about = "Remote install arguments")] -struct RemoteInstallArgs { - #[arg(short = 'b', long = "bin" )] - bin: bool, - #[arg(short = 'h', long = "http" )] - http: bool, - #[arg(short = 'c', long = "clean" )] - clean: bool +struct RemoteInstallArgs { + #[arg(short = 'b', long = "bin")] + bin: bool, + #[arg(short = 'h', long = "http")] + http: bool, + #[arg(short = 'c', long = "clean")] + clean: bool, } #[tokio::main] @@ -78,82 +67,90 @@ async fn main() -> Result<(), std::io::Error> { 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. + // It is necessary for me to understand the I/O model of the future mesk. match &cli.command { Commands::Validate { path } => { println!("Validating {}", path); - return Ok(()) - }, + return Ok(()); + } Commands::Build { pkgname } => { println!("Building {}", pkgname); - return Ok(()) - }, - Commands::Install { pkgname, source, args} => { + return Ok(()); + } + + Commands::Install { + pkgname, + source, + args, + } => { println!("Installing {}", pkgname); - return Ok(()) - }, + return Ok(()); + } Commands::Uninstall { pkgname } => { println!("Uninstalling {}", pkgname); - return Ok(()) - }, + return Ok(()); + } Commands::GetSource { pkgname } => { println!("Getting source of {}", pkgname); - return Ok(()) - }, - Commands::DefaultConfig { repo, cachedir, buildir } => { + return Ok(()); + } + Commands::DefaultConfig { + repo, + cachedir, + buildir, + } => { println!("Generating config file"); if cachedir.is_none() && repo.is_none() && buildir.is_none() { let config = Config::default().unwrap(); println!("---- Start of generated config ----"); - println!("{}", config); + println!("{}", config); println!("---- End of generated config ----"); - + log::warn!("Writing the default config to /etc/mesk/mesk.toml"); - + 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())?; println!("Config tool ending work."); - } else { - let config = Config::generate(repo, cachedir, buildir).unwrap(); + let config = Config::generate(repo, cachedir, buildir).unwrap(); println!("---- Start of generated config ----"); - println!("{:?}", config); + println!("{:?}", config); println!("---- End of generated config ----"); - + log::warn!("Writing the default config to /etc/mesk/mesk.toml"); - + let path = Path::new("/etc/mesk/mesk.toml"); create_dir_all(path.parent().unwrap())?; let mut file = File::create(path)?; file.write_all(config.as_bytes())?; println!("Config tool ending work."); - } - return Ok(()) - }, + return Ok(()); + } 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?; + let _index = I2PPackage::fetch_index(&mut i2pd).await?; println!("Index updated"); - return Ok(()) - }, + return Ok(()); + } Commands::Upgrade { pkgname } => { println!("Upgrading all packages"); - return Ok(()) - }, + return Ok(()); + } Commands::Credits => { - println!("CREATED BY: Asya and Namilsk as part of the Anthrill independent Global network distribution project"); + println!( + "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"); } } Ok(()) - -}
\ No newline at end of file +} diff --git a/src/net/http_packages.rs b/src/net/http_packages.rs index 1e3c6ad..fe1833d 100644 --- a/src/net/http_packages.rs +++ b/src/net/http_packages.rs @@ -1,19 +1,21 @@ +use crate::cfg::config::Config; use reqwest; use std::fs::File; use std::io::Write; use std::path::Path; -use crate::cfg::config::Config; +#[allow(dead_code)] pub struct HTTPPackage { config: Config, } +#[allow(dead_code)] impl HTTPPackage { /// Creates a new Downloader object with the given configuration. /// /// # Arguments /// - /// * `config` - The full mesk + /// * `config` - The full mesk /// /// # Returns /// @@ -33,12 +35,11 @@ impl HTTPPackage { /// # Returns /// /// A new Config object with the parsed configuration. If there's an error while reading or parsing the file, returns an Err containing a Box<dyn std::error::Error>. - pub fn parse_config(config_path: &str) -> Result<Config, Box<dyn std::error::Error>> { + pub fn parse_config() -> Result<Config, Box<dyn std::error::Error>> { let config = Config::parse()?; Ok(config) } - /// Downloads the INDEX.tar.gz file from the configured repository /// and stores it in the configured cache directory. /// @@ -58,11 +59,9 @@ impl HTTPPackage { let client = reqwest::Client::new(); - let response = client - .get(&index_url) - .send() - .await - .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, format!("Request failed: {}", e)))?; + let response = client.get(&index_url).send().await.map_err(|e| { + std::io::Error::new(std::io::ErrorKind::Other, format!("Request failed: {}", e)) + })?; if !response.status().is_success() { return Err(std::io::Error::new( @@ -74,19 +73,35 @@ impl HTTPPackage { let index_data = response .bytes() .await - .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, format!("Failed to read response body: {}", e)))? + .map_err(|e| { + std::io::Error::new( + std::io::ErrorKind::Other, + format!("Failed to read response body: {}", e), + ) + })? .to_vec(); let file_path = Path::new(cache_dir).join("INDEX.tar.gz"); - let mut file = File::create(&file_path) - .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, format!("Failed to create file: {}", e)))?; - file.write_all(&index_data) - .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, format!("Failed to write file: {}", e)))?; - file.flush() - .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, format!("Failed to flush file: {}", e)))?; + let mut file = File::create(&file_path).map_err(|e| { + std::io::Error::new( + std::io::ErrorKind::Other, + format!("Failed to create file: {}", e), + ) + })?; + file.write_all(&index_data).map_err(|e| { + std::io::Error::new( + std::io::ErrorKind::Other, + format!("Failed to write file: {}", e), + ) + })?; + file.flush().map_err(|e| { + std::io::Error::new( + std::io::ErrorKind::Other, + format!("Failed to flush file: {}", e), + ) + })?; Ok(true) } } - diff --git a/src/net/i2p_package.rs b/src/net/i2p_package.rs index 2776eea..d149454 100644 --- a/src/net/i2p_package.rs +++ b/src/net/i2p_package.rs @@ -1,33 +1,26 @@ - -use crate::cfg::config::Config; +use crate::cfg::config::Config; use tokio; -/* +/* use emissary_core::runtime::{ - AsyncRead, - AsyncWrite, + AsyncRead, + AsyncWrite, }; */ -use std::{fs::File, - path::Path, - io::Write}; -// use emissary_core::Profile; +use std::{fs::File, io::Write, path::Path}; +// use emissary_core::Profile; // use emissary_core::i2np::Message; -use tokio::io::{AsyncReadExt, - AsyncWriteExt, - BufReader}; +use tokio::io::{AsyncReadExt, AsyncWriteExt, BufReader}; +use url; +use yosemite::Session; use yosemite::SessionOptions; -use yosemite::{Session, style::Stream}; -use url; pub struct I2PPackage { config: Config, - } - impl I2PPackage { /// Creates a new I2P object with the given configuration. /// @@ -35,13 +28,9 @@ impl I2PPackage { /// /// 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, - } + I2PPackage { config: config } } - /// Downloads the INDEX.tar.gz file from the configured repository /// and stores it in the configured cache directory. /// @@ -52,11 +41,13 @@ impl I2PPackage { let repo_url_str = &self.config.repo.repo_url; let cache_dir = &self.config.paths.cache_dir; - let url = url::Url::parse(repo_url_str) - .map_err(|_| std::io::Error::new(std::io::ErrorKind::InvalidInput, "Invalid repo URL"))?; + let url = url::Url::parse(repo_url_str).map_err(|_| { + std::io::Error::new(std::io::ErrorKind::InvalidInput, "Invalid repo URL") + })?; - let host = url.host_str() - .ok_or_else(|| std::io::Error::new(std::io::ErrorKind::InvalidInput, "No host in URL"))?; + let host = url.host_str().ok_or_else(|| { + std::io::Error::new(std::io::ErrorKind::InvalidInput, "No host in URL") + })?; let request_path = url.path(); let request_path = if request_path.ends_with(".tar.gz") { @@ -65,55 +56,91 @@ impl I2PPackage { format!("{}/INDEX.tar.gz", request_path.trim_end_matches('/')) }; - let sam_host = "127.0.0.1"; - let sam_port = 7656; let session_options = SessionOptions::default(); - let mut session = Session::new(session_options).await - .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, format!("Failed to create SAM session: {}", e)))?; + let mut session = Session::new(session_options).await.map_err(|e| { + std::io::Error::new( + std::io::ErrorKind::Other, + format!("Failed to create SAM session: {}", e), + ) + })?; - let mut stream = session.connect(host).await - .map_err(|e| std::io::Error::new(std::io::ErrorKind::ConnectionAborted, format!("Failed to connect: {}", e)))?; + let mut stream = session.connect(host).await.map_err(|e| { + std::io::Error::new( + std::io::ErrorKind::ConnectionAborted, + format!("Failed to connect: {}", e), + ) + })?; let request = format!( "GET {} HTTP/1.1\r\nHost: {}\r\nConnection: close\r\n\r\n", request_path, host ); - - stream.write_all(request.as_bytes()).await - .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, format!("Failed to write request: {}", e)))?; + stream.write_all(request.as_bytes()).await.map_err(|e| { + std::io::Error::new( + std::io::ErrorKind::Other, + format!("Failed to write request: {}", e), + ) + })?; let mut reader = BufReader::new(stream); let mut response_buffer = Vec::new(); - reader.read_to_end(&mut response_buffer).await - .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, format!("Failed to read response: {}", e)))?; - + reader + .read_to_end(&mut response_buffer) + .await + .map_err(|e| { + std::io::Error::new( + std::io::ErrorKind::Other, + format!("Failed to read response: {}", e), + ) + })?; let headers_end = response_buffer .windows(4) .position(|window| window == b"\r\n\r\n") - .ok_or_else(|| std::io::Error::new(std::io::ErrorKind::InvalidData, "Invalid response: no headers end"))?; + .ok_or_else(|| { + std::io::Error::new( + std::io::ErrorKind::InvalidData, + "Invalid response: no headers end", + ) + })?; - let headers_str = std::str::from_utf8(&response_buffer[..headers_end]) - .map_err(|_| std::io::Error::new(std::io::ErrorKind::InvalidData, "Invalid header encoding"))?; + let headers_str = std::str::from_utf8(&response_buffer[..headers_end]).map_err(|_| { + std::io::Error::new(std::io::ErrorKind::InvalidData, "Invalid header encoding") + })?; if !headers_str.starts_with("HTTP/1.1 200") && !headers_str.starts_with("HTTP/1.0 200") { return Err(std::io::Error::new( std::io::ErrorKind::Other, - format!("HTTP Error: {}", headers_str.lines().next().unwrap_or("Unknown")), + format!( + "HTTP Error: {}", + headers_str.lines().next().unwrap_or("Unknown") + ), )); } let body_start = headers_end + 4; let file_path = Path::new(cache_dir).join("INDEX.tar.gz"); - - let mut file = File::create(&file_path) - .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, format!("Failed to create file: {}", e)))?; + let mut file = File::create(&file_path).map_err(|e| { + std::io::Error::new( + std::io::ErrorKind::Other, + format!("Failed to create file: {}", e), + ) + })?; file.write_all(&response_buffer[body_start..]) - .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, format!("Failed to write file: {}", e)))?; - file.flush() - .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, format!("Failed to flush file: {}", e)))?; + .map_err(|e| { + std::io::Error::new( + std::io::ErrorKind::Other, + format!("Failed to write file: {}", e), + ) + })?; + file.flush().map_err(|e| { + std::io::Error::new( + std::io::ErrorKind::Other, + format!("Failed to flush file: {}", e), + ) + })?; Ok(true) } diff --git a/src/net/mod.rs b/src/net/mod.rs index 54f5e7f..5a5aca0 100644 --- a/src/net/mod.rs +++ b/src/net/mod.rs @@ -1,2 +1,2 @@ -pub mod i2p_package; -pub mod http_packages;
\ No newline at end of file +pub mod http_packages; +pub mod i2p_package; diff --git a/src/pkgtoolkit/mod.rs b/src/pkgtoolkit/mod.rs index 4741892..63c952a 100644 --- a/src/pkgtoolkit/mod.rs +++ b/src/pkgtoolkit/mod.rs @@ -1 +1 @@ -pub mod pkgtools;
\ No newline at end of file +pub mod pkgtools; diff --git a/src/pkgtoolkit/pkgtools.rs b/src/pkgtoolkit/pkgtools.rs index cfd3d59..6c37595 100644 --- a/src/pkgtoolkit/pkgtools.rs +++ b/src/pkgtoolkit/pkgtools.rs @@ -2,23 +2,23 @@ use crate::cfg::config::Config; use std::{ - fs::{self, File, create_dir_all}, + fs::{self, File, create_dir_all}, io, - path::Path, - process::Command, - str, - os::unix::fs::PermissionsExt}; + os::unix::fs::PermissionsExt, + path::Path, + process::Command, + str, +}; + +// use emissary_core::i2np::tunnel::build; - use emissary_core::i2np::tunnel::build; - // use emissary_core::i2np::tunnel::build; use flate2::read::GzDecoder; use serde::{Deserialize, Serialize}; use tar::Archive; -use toml; -use cc; +use toml; #[derive(Serialize, Debug, Deserialize, Clone)] -pub enum archs { +pub enum Archs { X86_64, Aarch64, X86, @@ -28,9 +28,9 @@ pub enum archs { #[derive(Serialize, Debug, Deserialize, Clone)] pub struct Package { - name: String, - version: String, - arch: archs, + name: String, + version: String, + arch: Archs, descr: Option<String>, } @@ -38,9 +38,9 @@ pub struct Package { #[derive(Deserialize, Debug, Clone)] struct Install { package: Package, - path: String, - user: String, - group: String, + path: String, + user: String, + group: String, mode: String, //. Cancels the previous fields and installs them using the shell script custom_script: Option<String>, @@ -49,7 +49,7 @@ struct Install { #[allow(dead_code)] #[derive(Deserialize, Debug)] struct Setts { - env: Option<String>, // Export environment variables if this needed + env: Option<String>, // Export environment variables if this needed test: Option<String>, // Test the package after installation } @@ -57,32 +57,32 @@ struct Setts { pub enum BuildSystems { Make, CMake, - Meson, - Cargo + Meson, + Cargo, } #[allow(dead_code)] #[derive(Deserialize)] struct Build { - build_system: BuildSystems, + build_system: BuildSystems, env: Option<String>, script: Option<String>, } -impl archs { +impl Archs { fn as_str(&self) -> &'static str { match self { - archs::X86_64 => "x86_64", - archs::Aarch64 => "aarch64", - archs::X86 => "x86", - archs::ArmV7 => "armv7", - archs::ArmV8 => "armv8" + Archs::X86_64 => "x86_64", + Archs::Aarch64 => "aarch64", + Archs::X86 => "x86", + Archs::ArmV7 => "armv7", + Archs::ArmV8 => "armv8", } } } #[allow(dead_code)] -impl Package { +impl Package { /// Execute the build script for the package. /// /// This function takes the `Build` meta information as an argument and @@ -95,8 +95,8 @@ impl Package { 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)); + 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 => { @@ -105,33 +105,31 @@ impl Package { if build_meta.script.is_some() { c.arg("-f").arg(build_meta.script.as_ref().unwrap()); } - c.arg("all"); - c + c.arg("all"); + c } BuildSystems::CMake => { let build_dir_build = build_dir.join("build"); create_dir_all(&build_dir_build)?; let mut c = Command::new("cmake"); - c.arg("-S").arg(&build_dir) - .arg("-B").arg(&build_dir_build) - .current_dir(&build_dir); - c + c.arg("-S") + .arg(&build_dir) + .arg("-B") + .arg(&build_dir_build) + .current_dir(&build_dir); + c } BuildSystems::Meson => { let build_dir_build = build_dir.join("build"); create_dir_all(&build_dir_build)?; let mut c = Command::new("meson"); - c.arg("setup") - .arg(&build_dir_build) - .current_dir(&build_dir); - c + c.arg("setup").arg(&build_dir_build).current_dir(&build_dir); + c } BuildSystems::Cargo => { let mut c = Command::new("cargo"); - c.arg("build") - .arg("--release") - .current_dir(&build_dir); - c + c.arg("build").arg("--release").current_dir(&build_dir); + c } }; @@ -154,13 +152,13 @@ impl Package { } /// Extracts a .tar.gz archive to the cache directory specified in Config. - /// + /// /// This function handles opening the archive file, decompressing it with GzDecoder, /// and unpacking the contents into the configured cache directory. - /// + /// /// # Arguments /// * `path_to_archive` - A string representing the path to the .tar.gz file. - /// + /// /// # Returns /// * `Ok(())` if the archive is successfully unpacked. /// * `Err(std::io::Error)` if there's an issue opening, reading, or unpacking the archive. @@ -181,19 +179,22 @@ impl Package { /// /// This function parses the meta information from the .mesk archive, /// which includes the package name, version, architecture, description, - /// installation path, user, group, mode, and custom installation script + /// installation path, user, group, mode, and custom installation script /// and deserializing this information. Returns (Install, Option<Setts>, Option<Build>) - /// + /// /// The function expects the 'INSTALL', 'SETTS', and 'BUILD' files to be present /// in the `config.paths.cache_dir`. It specifically requires the 'INSTALL' file. - /// + /// /// # Errors /// /// 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. - fn loadmeta(minimal_package_meta: &mut Self) -> Result<(Install, Option<Setts>, Option<Build>), Box<dyn std::error::Error>> { // Changed return type for more flexibility - /* - Example INSTALL format: + fn loadmeta( + minimal_package_meta: &mut Self, + ) -> Result<(Install, Option<Setts>, Option<Build>), Box<dyn std::error::Error>> { + // Changed return type for more flexibility + /* + Example INSTALL format: [package] name = "my-package" version = "1.0.0" @@ -206,27 +207,26 @@ impl Package { group = "root" mode = "755" - # Also [install] can be + # Also [install] can be # path = "/usr/bin/my-package" # user = "root" # group = "root" # mode = "755" - # custom_script = "./install.sh" OR + # custom_script = "./install.sh" OR # custom_script = """ # echo "Installing my-package" # sudo apt-get install my-package # """ */ - let config = Config::parse()?; // Propagate error if parsing fails // Ensure the cache directory exists fs::create_dir_all(&config.paths.cache_dir)?; - let cache_dir = &config.paths.cache_dir; - let install_path = Path::new(cache_dir).join(format!("{}/INSTALL", minimal_package_meta.name)); + let install_path = + Path::new(cache_dir).join(format!("{}/INSTALL", minimal_package_meta.name)); let setts_path = Path::new(cache_dir).join(format!("{}/SETTS", minimal_package_meta.name)); let build_path = Path::new(cache_dir).join(format!("{}/BUILD", minimal_package_meta.name)); @@ -234,8 +234,9 @@ impl Package { if !install_path.exists() { return Err(io::Error::new( io::ErrorKind::NotFound, - "File INSTALL not found in cache directory" - ).into()); // Convert to Box<dyn Error> + "File INSTALL not found in cache directory", + ) + .into()); // Convert to Box<dyn Error> } // Read and deserialize the INSTALL file @@ -259,29 +260,33 @@ impl Package { } // Log if custom script is present - if let Some(ref script) = install_meta.custom_script { - println!("Custom script found for package: {}", install_meta.package.name); + if let Some(ref _script) = install_meta.custom_script { + println!( + "Custom script found for package: {}", + install_meta.package.name + ); // Consider logging the script content or just its presence based on verbosity // e.g., log::debug!("Custom script content: {}", script); } else { - println!("No custom script for package: {}", install_meta.package.name); + println!( + "No custom script for package: {}", + install_meta.package.name + ); } Ok((install_meta, setts_meta, build_meta)) } - - /// Checks if the archive contains INSTALL, SETTS and BUILD files. /// /// Checks if INSTALL file exists and is not empty. If it does not exist or is empty, returns an error. /// /// Checks if SETTS and BUILD files exist and are not empty. If they do not exist or are empty, logs a warning. - /// # Errors + /// # Errors /// * Returns an error if INSTALL file does not exist or is empty. /// * Returns an error if INSTALL file is empty. /// - // TODO: Add meta-files validation here. + // TODO: Add meta-files validation here. pub fn check(path_to_archive: String) -> Result<bool, std::io::Error> { // Call the new extraction function Self::extract_archive(&path_to_archive)?; @@ -325,27 +330,35 @@ impl Package { } } - let content = std::fs::read_to_string(&install_path) - .map_err(|e| { + let content = std::fs::read_to_string(&install_path).map_err(|e| { log::warn!("Failed to read file: {}", e); e })?; let install_content: Result<Install, toml::de::Error> = toml::from_str(&content); - log::info!("Validating arch..."); - if std::env::consts::ARCH != install_content.as_ref().map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidData, e.to_string()))?.package.arch.as_str() { + log::info!("Validating arch..."); + if std::env::consts::ARCH + != install_content + .as_ref() + .map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidData, e.to_string()))? + .package + .arch + .as_str() + { let pkg_arch = &install_content.unwrap().package.arch; // Safe because of previous check/unwrap - log::error!("Arch mismatch. Package arch: {:?}, Host arch: {}", pkg_arch, std::env::consts::ARCH); + log::error!( + "Arch mismatch. Package arch: {:?}, Host arch: {}", + pkg_arch, + std::env::consts::ARCH + ); return Err(std::io::Error::new( std::io::ErrorKind::InvalidData, "Arch mismatch", )); } - + Ok(true) } - - /// Builds the package according to the BUILD file in the archive. /// @@ -354,21 +367,21 @@ impl Package { /// If the BUILD file is empty and the INSTALL file contains a custom script, the custom script is run instead of the default install hook. /// If the BUILD file is not empty and the INSTALL file contains a custom script, the custom script is ignored. /// - /// + /// /// # Errors /// /// Returns an error if the BUILD file is invalid or if the build or install hook fails. pub fn build(&mut self) -> Result<bool, std::io::Error> { let meta = Self::loadmeta(self).unwrap(); let install_meta = meta.0; - let setts_meta = meta.1; + // let setts_meta = meta.1; let build_meta = meta.2; // BUILD NOT EMPTY. SOURCE: -> BUILD -> INSTALL -> SETTS // BUILD EMPTY. BIN: -> INSTALL -> SETTS enum Strategies { - BIN, - SOURCE + BIN, + SOURCE, } let strategy; //default @@ -380,7 +393,7 @@ impl Package { strategy = Strategies::SOURCE; log::info!("BUILD file is not empty. Skipping install, preparing to build"); } - + match strategy { Strategies::BIN => { if install_meta.custom_script.is_none() { @@ -389,11 +402,10 @@ impl Package { log::info!("Strategy: BIN; Running custom script."); let script = install_meta.custom_script.as_ref().unwrap(); if !script.starts_with("./") { - let _output = std::process::Command::new(format!("{}", script)); + let _output = std::process::Command::new(format!("{}", script)); } else { let _output = std::process::Command::new(format!("/bin/sh '{}'", script)); } - } } Strategies::SOURCE => { @@ -405,111 +417,152 @@ impl Package { Ok(true) } - /// Installs the package according to the INSTALL file in the archive. /// /// There are two strategies for installing the package. If the BUILD file is empty, the package is assumed to be a binary package and the default install hook is skipped. If the BUILD file is not empty, the package is assumed to be a source package and the default build hook is skipped. /// /// If the BUILD file is empty and the INSTALL file contains a custom script, the custom script is run instead of the default install hook. /// If the BUILD file is not empty and the INSTALL file contains a custom script, the custom script is ignored. - /// + /// /// # Errors /// /// 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()))?; - let (install_meta, _setts_meta, build_meta) = Self::loadmeta(self) - .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e.to_string()))?; - - - let is_build_present_and_not_empty = build_meta.is_some(); - - if is_build_present_and_not_empty { - log::info!("Found BUILD file, preparing to build and install package: {}", self.name); - let build_meta_ref = build_meta.as_ref().unwrap(); - self.execute_build(build_meta_ref); - - - if matches!(build_meta_ref.build_system, BuildSystems::Make) { - log::info!("Running 'make install' for package: {}", self.name); - let build_dir = Path::new(&config.paths.cache_dir) - .join(format!("{}-{}", self.name, self.version)); - let output = Command::new("make") - .arg("install") - .current_dir(&build_dir) - .output() - .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, format!("'make install' failed: {}", e)))?; - - if !output.status.success() { - let stderr = String::from_utf8_lossy(&output.stderr); - return Err(std::io::Error::new( + let config = Config::parse() + .map_err(|e| std::io::Error::new(std::io::ErrorKind::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()))?; + + let is_build_present_and_not_empty = build_meta.is_some(); + + if is_build_present_and_not_empty { + log::info!( + "Found BUILD file, preparing to build and install package: {}", + self.name + ); + let build_meta_ref = build_meta.as_ref().unwrap(); + let _ = self.execute_build(build_meta_ref); + + if matches!(build_meta_ref.build_system, BuildSystems::Make) { + log::info!("Running 'make install' for package: {}", self.name); + let build_dir = Path::new(&config.paths.cache_dir) + .join(format!("{}-{}", self.name, self.version)); + let output = Command::new("make") + .arg("install") + .current_dir(&build_dir) + .output() + .map_err(|e| { + std::io::Error::new( std::io::ErrorKind::Other, - format!("'make install' failed:\n{}", stderr), - )); - } + 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, + format!("'make install' failed:\n{}", stderr), + )); + } + } + } else { + log::info!( + "No BUILD file or it's empty. Treating as binary package. Installing via INSTALL config or custom script." + ); + // Установка бинарного пакета + if let Some(ref script) = install_meta.custom_script { + log::info!( + "Executing custom install script for {}", + install_meta.package.name + ); + let status = if script.starts_with("./") || script.contains('/') { + Command::new("/bin/sh").arg("-c").arg(script).status() + } else { + Command::new(script).status() + } + .map_err(|e| { + std::io::Error::new( + std::io::ErrorKind::Other, + format!("Failed to run custom script: {}", e), + ) + })?; + + if !status.success() { + return Err(std::io::Error::new( + std::io::ErrorKind::Other, + "Custom install script failed", + )); } } else { - log::info!("No BUILD file or it's empty. Treating as binary package. Installing via INSTALL config or custom script."); - // Установка бинарного пакета - if let Some(ref script) = install_meta.custom_script { - log::info!("Executing custom install script for {}", install_meta.package.name); - let status = if script.starts_with("./") || script.contains('/') { - Command::new("/bin/sh").arg("-c").arg(script).status() - } else { - Command::new(script).status() - } - .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, format!("Failed to run custom script: {}", e)))?; - - if !status.success() { - return Err(std::io::Error::new( + log::info!( + "No custom script. Running default install hook for {}", + install_meta.package.name + ); + // --- Дефолтный хук установки --- + // 1. Копируем файл из build_dir (предположим, что бинарный файл лежит в корне распакованного архива) + 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.path); + + // Убедимся, что целевая директория существует + if let Some(parent) = dest_path.parent() { + create_dir_all(parent).map_err(|e| { + std::io::Error::new( std::io::ErrorKind::Other, - "Custom install script failed", - )); - } - } else { - log::info!("No custom script. Running default install hook for {}", install_meta.package.name); - // --- Дефолтный хук установки --- - // 1. Копируем файл из build_dir (предположим, что бинарный файл лежит в корне распакованного архива) - 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.path); - - // Убедимся, что целевая директория существует - if let Some(parent) = dest_path.parent() { - create_dir_all(parent) - .map_err(|e| std::io::Error::new(std::io::ErrorKind::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, format!("Failed to copy file: {}", e)))?; - - let mode = u32::from_str_radix(&install_meta.mode, 8) - .map_err(|_| std::io::Error::new(std::io::ErrorKind::InvalidData, "Invalid mode string in INSTALL"))?; - let perms = PermissionsExt::from_mode(mode); - fs::set_permissions(dest_path, perms) - .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, format!("Failed to set permissions: {}", e)))?; - - let output = Command::new("chown") - .arg(&format!("{}:{}", install_meta.user, install_meta.group)) - .arg(dest_path) - .output() - .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, format!("'chown' command failed: {}", e)))?; + format!("Failed to create parent dir: {}", e), + ) + })?; + } - if !output.status.success() { - let stderr = String::from_utf8_lossy(&output.stderr); - log::warn!("Warning: 'chown' command failed (requires root?):\n{}", stderr); - } + fs::copy(&src_path, dest_path).map_err(|e| { + std::io::Error::new( + std::io::ErrorKind::Other, + format!("Failed to copy file: {}", e), + ) + })?; + + let mode = u32::from_str_radix(&install_meta.mode, 8).map_err(|_| { + std::io::Error::new( + std::io::ErrorKind::InvalidData, + "Invalid mode string in INSTALL", + ) + })?; + let perms = PermissionsExt::from_mode(mode); + fs::set_permissions(dest_path, perms).map_err(|e| { + std::io::Error::new( + std::io::ErrorKind::Other, + format!("Failed to set permissions: {}", e), + ) + })?; + + let output = Command::new("chown") + .arg(&format!("{}:{}", install_meta.user, install_meta.group)) + .arg(dest_path) + .output() + .map_err(|e| { + std::io::Error::new( + std::io::ErrorKind::Other, + format!("'chown' command failed: {}", e), + ) + })?; + + if !output.status.success() { + let stderr = String::from_utf8_lossy(&output.stderr); + log::warn!( + "Warning: 'chown' command failed (requires root?):\n{}", + stderr + ); } } + } log::info!("Package {} installed successfully.", self.name); Ok(true) } - pub fn gen_index() -> Result<bool, std::io::Error> { + pub fn gen_index() -> Result<bool, std::io::Error> { todo!(); - } -}
\ No newline at end of file +} |
