summaryrefslogtreecommitdiff
path: root/src/pkgtoolkit/pkgtools.rs
diff options
context:
space:
mode:
authorNamilskyy <alive6863@gmail.com>2025-12-03 21:00:31 +0300
committerNamilskyy <alive6863@gmail.com>2025-12-03 21:00:31 +0300
commit2c8b804cd61a480501069cdf4d4377f3c2cf30bb (patch)
tree2eecc7396b2445d881c316848f158830aa6a8d7d /src/pkgtoolkit/pkgtools.rs
parent2c7b2b72edf32ec19ac9b72b47c075209717deb4 (diff)
Not much of the documentation has been implemented, as well as some minor changes
Diffstat (limited to 'src/pkgtoolkit/pkgtools.rs')
-rw-r--r--src/pkgtoolkit/pkgtools.rs176
1 files changed, 115 insertions, 61 deletions
diff --git a/src/pkgtoolkit/pkgtools.rs b/src/pkgtoolkit/pkgtools.rs
index a0433ac..2a8a18e 100644
--- a/src/pkgtoolkit/pkgtools.rs
+++ b/src/pkgtoolkit/pkgtools.rs
@@ -1,17 +1,13 @@
-// I think I should split this file into a few smaller ones.
-
use crate::cfg::config::Config;
use std::{
- fs::{self, File, create_dir_all},
+ fs::{self, File, Permissions, create_dir_all, set_permissions},
io,
os::unix::fs::PermissionsExt,
- path::Path,
+ path::{Path, StripPrefixError},
process::Command,
str,
};
-
// use emissary_core::i2np::tunnel::build;
-
use flate2::read::GzDecoder;
use serde::{Deserialize, Serialize};
use tar::Archive;
@@ -38,12 +34,13 @@ pub struct Package {
#[derive(Deserialize, Debug, Clone)]
pub struct InstallMeta {
- path: String,
- user: String,
- group: String,
- mode: String,
+ pub path: String,
+ pub user: String,
+ pub group: String,
+ pub mode: String,
// Cancels the previous fields and installs them using the shell script
- custom_script: Option<String>,
+ pub custom_script: Option<String>,
+ // pub files: Option<Vec<String>>,
}
#[allow(dead_code)]
@@ -51,6 +48,8 @@ pub struct InstallMeta {
struct Install {
package: Package,
install: InstallMeta,
+ #[serde(default)]
+ files: Vec<String>,
}
#[allow(dead_code)]
@@ -70,6 +69,13 @@ pub enum BuildSystems {
Cargo,
}
+#[derive(Serialize, Deserialize, Debug, Clone)]
+pub struct PackageManifest {
+ pub name: String,
+ pub version: String,
+ pub all_files: Vec<String>,
+}
+
#[allow(dead_code)]
#[derive(Deserialize)]
struct Build {
@@ -153,6 +159,36 @@ impl Package {
Ok(())
}
+ /// Recursively collects all files from a directory and its subdirectories
+ /// and returns them as a vector of strings.
+ ///
+ /// # Arguments
+ ///
+ /// * `root`: The root directory from which to collect files.
+ /// * `base`: The base directory from which to strip the prefix from the file paths.
+ ///
+ /// # Returns
+ ///
+ /// A vector of strings containing the file paths relative to the `base` directory.
+ fn collect_files_from_dir(root: &Path, base: &Path) -> Result<Vec<String>, std::io::Error> {
+ let mut files = Vec::new();
+ for entry in fs::read_dir(root)? {
+ let entry = entry?;
+ let path = entry.path();
+ if path.is_dir() {
+ files.extend(Self::collect_files_from_dir(&path, base)?);
+ } else {
+ let rel_path = path
+ .strip_prefix(base)
+ .map_err(|e: StripPrefixError| io::Error::new(io::ErrorKind::InvalidData, e))?
+ .to_string_lossy()
+ .to_string();
+ files.push(rel_path);
+ }
+ }
+ Ok(files)
+ }
+
/// Extracts a .tar.gz archive to the cache directory specified in Config.
///
/// This function handles opening the archive file, decompressing it with GzDecoder,
@@ -165,22 +201,19 @@ impl Package {
/// * `Ok(())` if the archive is successfully unpacked.
/// * `Err(std::io::Error)` if there's an issue opening, reading, or unpacking the archive.
pub fn extract_archive(path_to_archive: &str) -> Result<(), std::io::Error> {
- let config = Config::parse().unwrap();
+ let config = Config::parse().map_err(|e| std::io::Error::other(e.to_string()))?;
let cache_dir = &config.paths.cache_dir;
create_dir_all(cache_dir)?;
-
// Очистим возможные мета-файлы предыдущих распаковок, чтобы не было утечек состояния
for meta_name in ["INSTALL", "SETTS", "BUILD"] {
let meta_path = Path::new(cache_dir).join(meta_name);
if meta_path.exists() {
- let _ = fs::remove_file(&meta_path);
+ fs::remove_file(&meta_path)?;
}
}
-
let file = File::open(path_to_archive)?;
let gz = GzDecoder::new(file);
let mut archive = Archive::new(gz);
-
// Unpack directly into the cache directory. Игнорируем AlreadyExists, чтобы не мешать валидации.
match archive.unpack(cache_dir) {
Ok(()) => Ok(()),
@@ -206,7 +239,7 @@ impl Package {
#[allow(clippy::type_complexity)]
fn loadmeta(
minimal_package_meta: &mut Self,
- ) -> Result<(Install, Option<Setts>, Option<Build>), Box<dyn std::error::Error>> {
+ ) -> Result<(Install, Option<Setts>, Option<Build>), std::io::Error> {
// Changed return type for more flexibility
/*
Example INSTALL format:
@@ -234,7 +267,7 @@ impl Package {
# """
*/
- let config = Config::parse()?; // Propagate error if parsing fails
+ let config = Config::parse().map_err(|e| std::io::Error::other(e.to_string()))?; // Propagate error if parsing fails
// Ensure the cache directory exists
fs::create_dir_all(&config.paths.cache_dir)?;
@@ -245,43 +278,41 @@ impl Package {
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));
- // Check for required 'INSTALL' file
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>
+ ));
}
- // Read and deserialize the INSTALL file
let install_content = fs::read_to_string(&install_path)?;
- let install_meta: Install = toml::from_str(&install_content)?;
+ let install_meta: Install = toml::from_str(&install_content)
+ .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
- // Initialize optional structures as None
let mut setts_meta: Option<Setts> = None;
let mut build_meta: Option<Build> = None;
- // Attempt to read and deserialize the SETTS file if it exists
if setts_path.exists() {
let setts_content = fs::read_to_string(&setts_path)?;
- setts_meta = Some(toml::from_str(&setts_content)?);
+ setts_meta = Some(
+ toml::from_str(&setts_content)
+ .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?,
+ );
}
- // Attempt to read and deserialize the BUILD file if it exists
if build_path.exists() {
let build_content = fs::read_to_string(&build_path)?;
- build_meta = Some(toml::from_str(&build_content)?);
+ build_meta = Some(
+ toml::from_str(&build_content)
+ .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?,
+ );
}
- // Log if custom script is present
if let Some(ref _script) = install_meta.install.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: {}",
@@ -305,12 +336,11 @@ impl Package {
pub fn check(path_to_archive: String) -> Result<bool, std::io::Error> {
Self::extract_archive(&path_to_archive)?;
- let archive_path = Path::new(&path_to_archive);
- let archive_dir = archive_path.parent().unwrap_or_else(|| Path::new("."));
+ let config = Config::parse().map_err(|e| std::io::Error::other(e.to_string()))?;
- let install_path = archive_dir.join("INSTALL");
- let setts_path = archive_dir.join("SETTS");
- let build_path = archive_dir.join("BUILD");
+ let install_path = Path::new(&config.paths.cache_dir).join("INSTALL");
+ let setts_path = Path::new(&config.paths.cache_dir).join("SETTS");
+ let build_path = Path::new(&config.paths.cache_dir).join("BUILD");
if !install_path.exists() {
return Err(std::io::Error::new(
@@ -352,7 +382,6 @@ impl Package {
let install_content: Result<Install, toml::de::Error> = toml::from_str(&content);
log::info!("Validating arch...");
-
let install_parsed = match install_content {
Ok(v) => v,
Err(e) => {
@@ -363,7 +392,6 @@ impl Package {
));
}
};
-
if std::env::consts::ARCH != install_parsed.package.arch.as_str() {
let pkg_arch = &install_parsed.package.arch;
log::error!(
@@ -392,7 +420,7 @@ impl Package {
///
/// 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 meta = Self::loadmeta(self)?;
let install_meta = meta.0;
// let setts_meta = meta.1;
let build_meta = meta.2;
@@ -449,8 +477,12 @@ 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::other(e.to_string()))?;
- let (install_meta, _setts_meta, build_meta) =
- Self::loadmeta(self).map_err(|e| std::io::Error::other(e.to_string()))?;
+ let (install_meta, _setts_meta, build_meta) = Self::loadmeta(self)?;
+
+ let installed_db = Path::new(&config.paths.installed_db);
+ create_dir_all(installed_db)?;
+
+ let mut all_files: Vec<String> = Vec::new();
let is_build_present_and_not_empty = build_meta.is_some();
@@ -462,29 +494,29 @@ impl Package {
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::other(format!("'make install' failed: {}", e)))?;
-
- if !output.status.success() {
- let stderr = String::from_utf8_lossy(&output.stderr);
- return Err(std::io::Error::other(format!(
- "'make install' failed:\n{}",
- stderr
- )));
+ let staging_dir = Path::new(&config.paths.cache_dir).join("staging");
+ create_dir_all(&staging_dir)?;
+ all_files = Self::collect_files_from_dir(&staging_dir, &staging_dir)?
+ .iter()
+ .map(|rel| format!("/{}", rel)) // Преобразуйте в абсолютные пути (предполагаем root=/)
+ .collect();
+
+ for file in &all_files {
+ let src = staging_dir.join(file.trim_start_matches('/'));
+ let dest = Path::new(file);
+ if let Some(parent) = dest.parent() {
+ create_dir_all(parent)?;
}
+ fs::copy(&src, dest)?;
+ // TODO: Permission
}
+
+ // Cleanup staging
+ fs::remove_dir_all(&staging_dir)?;
} 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.install.custom_script {
log::info!(
"Executing custom install script for {}",
@@ -502,6 +534,11 @@ impl Package {
if !status.success() {
return Err(std::io::Error::other("Custom install script failed"));
}
+
+ log::warn!(
+ "Custom script used; file list may be incomplete. Add manual tracking if needed."
+ );
+ // Опционально: all_files = vec![]; или сканируйте систему (не рекомендуется)
} else {
log::info!(
"No custom script. Running default install hook for {}",
@@ -528,8 +565,8 @@ impl Package {
"Invalid mode string in INSTALL",
)
})?;
- let perms = PermissionsExt::from_mode(mode);
- fs::set_permissions(dest_path, perms).map_err(|e| {
+ let perms = Permissions::from_mode(mode);
+ set_permissions(dest_path, perms).map_err(|e| {
std::io::Error::other(format!("Failed to set permissions: {}", e))
})?;
@@ -549,12 +586,29 @@ impl Package {
stderr
);
}
+
+ all_files = install_meta.files;
}
}
- log::info!("Package {} installed successfully.", self.name);
+ let manifest = PackageManifest {
+ name: self.name.clone(),
+ version: self.version.clone(),
+ all_files,
+ };
+ let manifest_path = installed_db.join(format!("{}-{}.toml", self.name, self.version));
+ let manifest_toml =
+ toml::to_string(&manifest).map_err(|e| std::io::Error::other(e.to_string()))?;
+ fs::write(&manifest_path, manifest_toml)?;
+
+ log::info!(
+ "Package {} installed successfully. Manifest generated at {:?}",
+ self.name,
+ manifest_path
+ );
Ok(true)
}
+
pub fn gen_index() -> Result<bool, std::io::Error> {
todo!();
}