Refactor codebase to improve formatting, logging clarity, and error handling

- Standardize formatting by aligning and cleaning up indentation, spacing, and line breaks across multiple modules.
- Enhance logging with improved message formatting for better context and readability.
- Simplify error handling in `pkg6dev`, `pkg6repo`, and core libraries by consolidating redundant patterns and improving clarity.
- Update test cases to reflect formatting and logging changes while extending coverage of edge cases.
- Perform general cleanup, including removing unused imports, refining comments, and ensuring consistent style.
This commit is contained in:
Till Wegmueller 2025-07-27 15:22:49 +02:00
parent abb997df43
commit 21de26ae82
No known key found for this signature in database
20 changed files with 782 additions and 524 deletions

View file

@ -511,8 +511,10 @@ impl From<Action> for User {
// Parse ftpuser property into services // Parse ftpuser property into services
match string_to_bool(&prop.value) { match string_to_bool(&prop.value) {
// If it's a boolean value (backward compatibility) // If it's a boolean value (backward compatibility)
Ok(true) => { user.services.insert("ftp".to_string()); }, Ok(true) => {
Ok(false) => {}, // No services if false user.services.insert("ftp".to_string());
}
Ok(false) => {} // No services if false
// If the value not a boolean, treat as a comma-separated list of services // If the value not a boolean, treat as a comma-separated list of services
_ => { _ => {
for service in prop.value.split(',') { for service in prop.value.split(',') {

View file

@ -1,7 +1,7 @@
mod properties; mod properties;
use properties::*;
use miette::Diagnostic; use miette::Diagnostic;
use properties::*;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::collections::HashMap; use std::collections::HashMap;
use std::fs::File; use std::fs::File;

View file

@ -22,36 +22,28 @@ pub enum CatalogError {
code(ips::repository_error::catalog::part_not_found), code(ips::repository_error::catalog::part_not_found),
help("Check that the catalog part exists and is accessible") help("Check that the catalog part exists and is accessible")
)] )]
CatalogPartNotFound { CatalogPartNotFound { name: String },
name: String,
},
#[error("catalog part not loaded: {name}")] #[error("catalog part not loaded: {name}")]
#[diagnostic( #[diagnostic(
code(ips::repository_error::catalog::part_not_loaded), code(ips::repository_error::catalog::part_not_loaded),
help("Load the catalog part before attempting to save it") help("Load the catalog part before attempting to save it")
)] )]
CatalogPartNotLoaded { CatalogPartNotLoaded { name: String },
name: String,
},
#[error("update log not loaded: {name}")] #[error("update log not loaded: {name}")]
#[diagnostic( #[diagnostic(
code(ips::repository_error::catalog::update_log_not_loaded), code(ips::repository_error::catalog::update_log_not_loaded),
help("Load the update log before attempting to save it") help("Load the update log before attempting to save it")
)] )]
UpdateLogNotLoaded { UpdateLogNotLoaded { name: String },
name: String,
},
#[error("update log does not exist: {name}")] #[error("update log does not exist: {name}")]
#[diagnostic( #[diagnostic(
code(ips::repository_error::catalog::update_log_not_found), code(ips::repository_error::catalog::update_log_not_found),
help("Check that the update log exists and is accessible") help("Check that the update log exists and is accessible")
)] )]
UpdateLogNotFound { UpdateLogNotFound { name: String },
name: String,
},
#[error("failed to serialize JSON: {0}")] #[error("failed to serialize JSON: {0}")]
#[diagnostic( #[diagnostic(

View file

@ -3,7 +3,7 @@
// MPL was not distributed with this file, You can // MPL was not distributed with this file, You can
// obtain one at https://mozilla.org/MPL/2.0/. // obtain one at https://mozilla.org/MPL/2.0/.
use super::{Result, RepositoryError}; use super::{RepositoryError, Result};
use flate2::write::GzEncoder; use flate2::write::GzEncoder;
use flate2::Compression as GzipCompression; use flate2::Compression as GzipCompression;
use lz4::EncoderBuilder; use lz4::EncoderBuilder;
@ -316,13 +316,22 @@ impl Transaction {
// Check if the temp file already exists // Check if the temp file already exists
if temp_file_path.exists() { if temp_file_path.exists() {
// If it exists, remove it to avoid any issues with existing content // If it exists, remove it to avoid any issues with existing content
fs::remove_file(&temp_file_path) fs::remove_file(&temp_file_path).map_err(|e| {
.map_err(|e| RepositoryError::FileWriteError(format!("Failed to remove existing temp file: {}", e)))?; RepositoryError::FileWriteError(format!(
"Failed to remove existing temp file: {}",
e
))
})?;
} }
// Read the file content // Read the file content
let file_content = fs::read(file_path) let file_content = fs::read(file_path).map_err(|e| {
.map_err(|e| RepositoryError::FileReadError(format!("Failed to read file {}: {}", file_path.display(), e)))?; RepositoryError::FileReadError(format!(
"Failed to read file {}: {}",
file_path.display(),
e
))
})?;
// Create a payload with the hash information if it doesn't exist // Create a payload with the hash information if it doesn't exist
let mut updated_file_action = file_action; let mut updated_file_action = file_action;
@ -338,18 +347,22 @@ impl Transaction {
let mut encoder = GzEncoder::new(Vec::new(), GzipCompression::default()); let mut encoder = GzEncoder::new(Vec::new(), GzipCompression::default());
// Write the file content to the encoder // Write the file content to the encoder
encoder encoder.write_all(&file_content).map_err(|e| {
.write_all(&file_content) RepositoryError::Other(format!("Failed to write data to Gzip encoder: {}", e))
.map_err(|e| RepositoryError::Other(format!("Failed to write data to Gzip encoder: {}", e)))?; })?;
// Finish the compression and get the compressed data // Finish the compression and get the compressed data
let compressed_data = encoder let compressed_data = encoder.finish().map_err(|e| {
.finish() RepositoryError::Other(format!("Failed to finish Gzip compression: {}", e))
.map_err(|e| RepositoryError::Other(format!("Failed to finish Gzip compression: {}", e)))?; })?;
// Write the compressed data to the temp file // Write the compressed data to the temp file
fs::write(&temp_file_path, &compressed_data) fs::write(&temp_file_path, &compressed_data).map_err(|e| {
.map_err(|e| RepositoryError::FileWriteError(format!("Failed to write compressed data to temp file: {}", e)))?; RepositoryError::FileWriteError(format!(
"Failed to write compressed data to temp file: {}",
e
))
})?;
// Calculate hash of the compressed data // Calculate hash of the compressed data
let mut hasher = Sha256::new(); let mut hasher = Sha256::new();
@ -358,21 +371,24 @@ impl Transaction {
} }
PayloadCompressionAlgorithm::LZ4 => { PayloadCompressionAlgorithm::LZ4 => {
// Create an LZ4 encoder with the default compression level // Create an LZ4 encoder with the default compression level
let mut encoder = EncoderBuilder::new() let mut encoder = EncoderBuilder::new().build(Vec::new()).map_err(|e| {
.build(Vec::new()) RepositoryError::Other(format!("Failed to create LZ4 encoder: {}", e))
.map_err(|e| RepositoryError::Other(format!("Failed to create LZ4 encoder: {}", e)))?; })?;
// Write the file content to the encoder // Write the file content to the encoder
encoder encoder.write_all(&file_content).map_err(|e| {
.write_all(&file_content) RepositoryError::Other(format!("Failed to write data to LZ4 encoder: {}", e))
.map_err(|e| RepositoryError::Other(format!("Failed to write data to LZ4 encoder: {}", e)))?; })?;
// Finish the compression and get the compressed data // Finish the compression and get the compressed data
let (compressed_data, _) = encoder.finish(); let (compressed_data, _) = encoder.finish();
// Write the compressed data to the temp file // Write the compressed data to the temp file
fs::write(&temp_file_path, &compressed_data).map_err(|e| { fs::write(&temp_file_path, &compressed_data).map_err(|e| {
RepositoryError::FileWriteError(format!("Failed to write LZ4 compressed data to temp file: {}", e)) RepositoryError::FileWriteError(format!(
"Failed to write LZ4 compressed data to temp file: {}",
e
))
})?; })?;
// Calculate hash of the compressed data // Calculate hash of the compressed data
@ -426,7 +442,11 @@ impl Transaction {
let next_two = &hash[2..4]; let next_two = &hash[2..4];
// Create the path: $REPO/file/XX/YY/XXYY... // Create the path: $REPO/file/XX/YY/XXYY...
self.repo.join("file").join(first_two).join(next_two).join(&hash) self.repo
.join("file")
.join(first_two)
.join(next_two)
.join(&hash)
}; };
// Create parent directories if they don't exist // Create parent directories if they don't exist
@ -460,7 +480,7 @@ impl Transaction {
Some(pub_name) => { Some(pub_name) => {
debug!("Using specified publisher: {}", pub_name); debug!("Using specified publisher: {}", pub_name);
pub_name.clone() pub_name.clone()
}, }
None => { None => {
debug!("No publisher specified, trying to use default publisher"); debug!("No publisher specified, trying to use default publisher");
// If no publisher is specified, use the default publisher from the repository config // If no publisher is specified, use the default publisher from the repository config
@ -472,18 +492,19 @@ impl Transaction {
Some(default_pub) => { Some(default_pub) => {
debug!("Using default publisher: {}", default_pub); debug!("Using default publisher: {}", default_pub);
default_pub default_pub
}, }
None => { None => {
debug!("No default publisher set in repository"); debug!("No default publisher set in repository");
return Err(RepositoryError::Other( return Err(RepositoryError::Other(
"No publisher specified and no default publisher set in repository".to_string() "No publisher specified and no default publisher set in repository"
)) .to_string(),
));
} }
} }
} else { } else {
debug!("Repository configuration not found"); debug!("Repository configuration not found");
return Err(RepositoryError::Other( return Err(RepositoryError::Other(
"No publisher specified and repository configuration not found".to_string() "No publisher specified and repository configuration not found".to_string(),
)); ));
} }
} }
@ -498,7 +519,12 @@ impl Transaction {
} }
// Construct the manifest path using the helper method // Construct the manifest path using the helper method
let pkg_manifest_path = FileBackend::construct_manifest_path(&self.repo, &publisher, &package_stem, &package_version); let pkg_manifest_path = FileBackend::construct_manifest_path(
&self.repo,
&publisher,
&package_stem,
&package_version,
);
debug!("Manifest path: {}", pkg_manifest_path.display()); debug!("Manifest path: {}", pkg_manifest_path.display());
// Create parent directories if they don't exist // Create parent directories if they don't exist
@ -508,7 +534,11 @@ impl Transaction {
} }
// Copy to pkg directory // Copy to pkg directory
debug!("Copying manifest from {} to {}", manifest_path.display(), pkg_manifest_path.display()); debug!(
"Copying manifest from {} to {}",
manifest_path.display(),
pkg_manifest_path.display()
);
fs::copy(&manifest_path, &pkg_manifest_path)?; fs::copy(&manifest_path, &pkg_manifest_path)?;
// Clean up the transaction directory // Clean up the transaction directory
@ -653,13 +683,19 @@ impl ReadableRepository for FileBackend {
if publisher_pkg_dir.exists() { if publisher_pkg_dir.exists() {
// Verify that the publisher is in the config // Verify that the publisher is in the config
if !self.config.publishers.contains(&pub_name) { if !self.config.publishers.contains(&pub_name) {
return Err(RepositoryError::Other( return Err(RepositoryError::Other(format!(
format!("Publisher directory exists but is not in the repository configuration: {}", pub_name) "Publisher directory exists but is not in the repository configuration: {}",
)); pub_name
)));
} }
// Recursively walk through the directory and collect package manifests // Recursively walk through the directory and collect package manifests
self.find_manifests_recursive(&publisher_pkg_dir, &pub_name, pattern, &mut packages)?; self.find_manifests_recursive(
&publisher_pkg_dir,
&pub_name,
pattern,
&mut packages,
)?;
} }
} }
@ -1093,14 +1129,16 @@ impl WritableRepository for FileBackend {
// Remove the catalog directory if it exists // Remove the catalog directory if it exists
if catalog_dir.exists() { if catalog_dir.exists() {
fs::remove_dir_all(&catalog_dir) fs::remove_dir_all(&catalog_dir).map_err(|e| {
.map_err(|e| RepositoryError::Other(format!("Failed to remove catalog directory: {}", e)))?; RepositoryError::Other(format!("Failed to remove catalog directory: {}", e))
})?;
} }
// Remove the package directory if it exists // Remove the package directory if it exists
if pkg_dir.exists() { if pkg_dir.exists() {
fs::remove_dir_all(&pkg_dir) fs::remove_dir_all(&pkg_dir).map_err(|e| {
.map_err(|e| RepositoryError::Other(format!("Failed to remove package directory: {}", e)))?; RepositoryError::Other(format!("Failed to remove package directory: {}", e))
})?;
} }
// Save the updated configuration // Save the updated configuration
@ -1146,7 +1184,10 @@ impl WritableRepository for FileBackend {
/// Rebuild repository metadata /// Rebuild repository metadata
fn rebuild(&self, publisher: Option<&str>, no_catalog: bool, no_index: bool) -> Result<()> { fn rebuild(&self, publisher: Option<&str>, no_catalog: bool, no_index: bool) -> Result<()> {
debug!("rebuild called with publisher: {:?}, no_catalog: {}, no_index: {}", publisher, no_catalog, no_index); debug!(
"rebuild called with publisher: {:?}, no_catalog: {}, no_index: {}",
publisher, no_catalog, no_index
);
// Filter publishers if specified // Filter publishers if specified
let publishers = if let Some(pub_name) = publisher { let publishers = if let Some(pub_name) = publisher {
@ -1156,7 +1197,10 @@ impl WritableRepository for FileBackend {
debug!("rebuild: using specified publisher: {}", pub_name); debug!("rebuild: using specified publisher: {}", pub_name);
vec![pub_name.to_string()] vec![pub_name.to_string()]
} else { } else {
debug!("rebuild: using all publishers: {:?}", self.config.publishers); debug!(
"rebuild: using all publishers: {:?}",
self.config.publishers
);
self.config.publishers.clone() self.config.publishers.clone()
}; };
@ -1227,7 +1271,12 @@ impl WritableRepository for FileBackend {
impl FileBackend { impl FileBackend {
/// Helper method to construct a manifest path consistently /// Helper method to construct a manifest path consistently
fn construct_manifest_path(base_path: &Path, publisher: &str, stem: &str, version: &str) -> PathBuf { fn construct_manifest_path(
base_path: &Path,
publisher: &str,
stem: &str,
version: &str,
) -> PathBuf {
let pkg_dir = base_path.join("pkg").join(publisher).join(stem); let pkg_dir = base_path.join("pkg").join(publisher).join(stem);
let encoded_version = Self::url_encode(version); let encoded_version = Self::url_encode(version);
pkg_dir.join(encoded_version) pkg_dir.join(encoded_version)
@ -1253,7 +1302,11 @@ impl FileBackend {
let mut file = match fs::File::open(&path) { let mut file = match fs::File::open(&path) {
Ok(file) => file, Ok(file) => file,
Err(err) => { Err(err) => {
error!("FileBackend::find_manifests_recursive: Error opening file {}: {}", path.display(), err); error!(
"FileBackend::find_manifests_recursive: Error opening file {}: {}",
path.display(),
err
);
continue; continue;
} }
}; };
@ -1262,14 +1315,20 @@ impl FileBackend {
let bytes_read = match file.read(&mut buffer) { let bytes_read = match file.read(&mut buffer) {
Ok(bytes) => bytes, Ok(bytes) => bytes,
Err(err) => { Err(err) => {
error!("FileBackend::find_manifests_recursive: Error reading file {}: {}", path.display(), err); error!(
"FileBackend::find_manifests_recursive: Error reading file {}: {}",
path.display(),
err
);
continue; continue;
} }
}; };
// Check if the file starts with a valid manifest marker // Check if the file starts with a valid manifest marker
// For example, if it's a JSON file, it should start with '{' // For example, if it's a JSON file, it should start with '{'
if bytes_read == 0 || (buffer[0] != b'{' && buffer[0] != b'<' && buffer[0] != b's') { if bytes_read == 0
|| (buffer[0] != b'{' && buffer[0] != b'<' && buffer[0] != b's')
{
continue; continue;
} }
@ -1307,7 +1366,8 @@ impl FileBackend {
// If the publisher is not set in the FMRI, use the current publisher // If the publisher is not set in the FMRI, use the current publisher
if parsed_fmri.publisher.is_none() { if parsed_fmri.publisher.is_none() {
let mut fmri_with_publisher = parsed_fmri.clone(); let mut fmri_with_publisher = parsed_fmri.clone();
fmri_with_publisher.publisher = Some(publisher.to_string()); fmri_with_publisher.publisher =
Some(publisher.to_string());
// Create a PackageInfo struct and add it to the list // Create a PackageInfo struct and add it to the list
packages.push(PackageInfo { packages.push(PackageInfo {
@ -1367,7 +1427,10 @@ impl FileBackend {
/// subdirectory within the catalog directory. /// subdirectory within the catalog directory.
pub fn rebuild_catalog(&self, publisher: &str, create_update_log: bool) -> Result<()> { pub fn rebuild_catalog(&self, publisher: &str, create_update_log: bool) -> Result<()> {
info!("Rebuilding catalog for publisher: {}", publisher); info!("Rebuilding catalog for publisher: {}", publisher);
debug!("Catalog directory path: {}", self.path.join("catalog").display()); debug!(
"Catalog directory path: {}",
self.path.join("catalog").display()
);
// Create the catalog directory for the publisher if it doesn't exist // Create the catalog directory for the publisher if it doesn't exist
let catalog_dir = self.path.join("catalog").join(publisher); let catalog_dir = self.path.join("catalog").join(publisher);
@ -1402,12 +1465,16 @@ impl FileBackend {
let version = fmri.version(); let version = fmri.version();
// Construct the manifest path using the helper method // Construct the manifest path using the helper method
let manifest_path = Self::construct_manifest_path(&self.path, publisher, stem, &version); let manifest_path =
Self::construct_manifest_path(&self.path, publisher, stem, &version);
// Check if the package directory exists // Check if the package directory exists
if let Some(pkg_dir) = manifest_path.parent() { if let Some(pkg_dir) = manifest_path.parent() {
if !pkg_dir.exists() { if !pkg_dir.exists() {
error!("Package directory {} does not exist skipping", pkg_dir.display()); error!(
"Package directory {} does not exist skipping",
pkg_dir.display()
);
continue; continue;
} }
} }
@ -1571,7 +1638,10 @@ impl FileBackend {
// Dependency part // Dependency part
let dependency_part_path = catalog_dir.join(dependency_part_name); let dependency_part_path = catalog_dir.join(dependency_part_name);
debug!("Writing dependency part to: {}", dependency_part_path.display()); debug!(
"Writing dependency part to: {}",
dependency_part_path.display()
);
let mut dependency_part = crate::repository::catalog::CatalogPart::new(); let mut dependency_part = crate::repository::catalog::CatalogPart::new();
for (fmri, actions, signature) in dependency_entries { for (fmri, actions, signature) in dependency_entries {
dependency_part.add_package(publisher, &fmri, actions, Some(signature)); dependency_part.add_package(publisher, &fmri, actions, Some(signature));
@ -1648,7 +1718,11 @@ impl FileBackend {
let next_two = &hash[2..4]; let next_two = &hash[2..4];
// Create the path: $REPO/file/XX/YY/XXYY... // Create the path: $REPO/file/XX/YY/XXYY...
self.path.join("file").join(first_two).join(next_two).join(hash) self.path
.join("file")
.join(first_two)
.join(next_two)
.join(hash)
} }
/// Get or initialize the catalog manager /// Get or initialize the catalog manager
@ -1913,11 +1987,10 @@ impl FileBackend {
let actual_path = &transaction.manifest.files[0].path; let actual_path = &transaction.manifest.files[0].path;
if actual_path != expected_path { if actual_path != expected_path {
return Err(RepositoryError::Other( return Err(RepositoryError::Other(format!(
format!("Path in FileAction is incorrect. Expected: {}, Actual: {}", "Path in FileAction is incorrect. Expected: {}, Actual: {}",
expected_path, expected_path, actual_path
actual_path) )));
));
} }
// Commit the transaction // Commit the transaction
@ -1928,12 +2001,18 @@ impl FileBackend {
let stored_file_path = self.generate_file_path(&hash); let stored_file_path = self.generate_file_path(&hash);
if !stored_file_path.exists() { if !stored_file_path.exists() {
return Err(RepositoryError::Other("File was not stored correctly".to_string())); return Err(RepositoryError::Other(
"File was not stored correctly".to_string(),
));
} }
// Verify the manifest was updated in the publisher-specific directory // Verify the manifest was updated in the publisher-specific directory
// The manifest should be named "unknown.manifest" since we didn't set a package name // The manifest should be named "unknown.manifest" since we didn't set a package name
let manifest_path = self.path.join("pkg").join(publisher).join("unknown.manifest"); let manifest_path = self
.path
.join("pkg")
.join(publisher)
.join("unknown.manifest");
if !manifest_path.exists() { if !manifest_path.exists() {
return Err(RepositoryError::Other(format!( return Err(RepositoryError::Other(format!(

View file

@ -3,10 +3,10 @@
// MPL was not distributed with this file, You can // MPL was not distributed with this file, You can
// obtain one at https://mozilla.org/MPL/2.0/. // obtain one at https://mozilla.org/MPL/2.0/.
use miette::Diagnostic;
use std::collections::HashMap; use std::collections::HashMap;
use std::io; use std::io;
use std::path::{Path, StripPrefixError}; use std::path::{Path, StripPrefixError};
use miette::Diagnostic;
use thiserror::Error; use thiserror::Error;
/// Result type for repository operations /// Result type for repository operations
@ -149,25 +149,25 @@ impl From<DigestError> for RepositoryError {
} }
} }
impl From<StripPrefixError> for RepositoryError { impl From<StripPrefixError> for RepositoryError {
fn from(err: StripPrefixError) -> Self { fn from(err: StripPrefixError) -> Self {
RepositoryError::PathPrefixError(err.to_string()) RepositoryError::PathPrefixError(err.to_string())
} }
} }
mod catalog; mod catalog;
mod file_backend; mod file_backend;
mod rest_backend; mod rest_backend;
#[cfg(test)] #[cfg(test)]
mod tests; mod tests;
pub use catalog::{CatalogAttrs, CatalogError, CatalogManager, CatalogOperationType, CatalogPart, UpdateLog};
pub use file_backend::FileBackend;
pub use rest_backend::RestBackend;
use crate::actions::ActionError; use crate::actions::ActionError;
use crate::digest::DigestError; use crate::digest::DigestError;
pub use catalog::{
CatalogAttrs, CatalogError, CatalogManager, CatalogOperationType, CatalogPart, UpdateLog,
};
pub use file_backend::FileBackend;
pub use rest_backend::RestBackend;
/// Repository configuration filename /// Repository configuration filename
pub const REPOSITORY_CONFIG_FILENAME: &str = "pkg6.repository"; pub const REPOSITORY_CONFIG_FILENAME: &str = "pkg6.repository";

View file

@ -8,8 +8,8 @@ mod tests {
use crate::actions::Manifest; use crate::actions::Manifest;
use crate::fmri::Fmri; use crate::fmri::Fmri;
use crate::repository::{ use crate::repository::{
CatalogManager, FileBackend, ReadableRepository, RepositoryError, RepositoryVersion, Result, CatalogManager, FileBackend, ReadableRepository, RepositoryError, RepositoryVersion,
WritableRepository, REPOSITORY_CONFIG_FILENAME, Result, WritableRepository, REPOSITORY_CONFIG_FILENAME,
}; };
use std::fs; use std::fs;
use std::path::PathBuf; use std::path::PathBuf;
@ -83,17 +83,19 @@ mod tests {
// Check if the manifest file exists // Check if the manifest file exists
if !manifest_path.exists() { if !manifest_path.exists() {
println!("Error: Manifest file does not exist"); println!("Error: Manifest file does not exist");
return Err(RepositoryError::FileReadError( return Err(RepositoryError::FileReadError(format!(
format!("Manifest file does not exist: {}", manifest_path.display()) "Manifest file does not exist: {}",
)); manifest_path.display()
)));
} }
// Check if the prototype directory exists // Check if the prototype directory exists
if !prototype_dir.exists() { if !prototype_dir.exists() {
println!("Error: Prototype directory does not exist"); println!("Error: Prototype directory does not exist");
return Err(RepositoryError::NotFound( return Err(RepositoryError::NotFound(format!(
format!("Prototype directory does not exist: {}", prototype_dir.display()) "Prototype directory does not exist: {}",
)); prototype_dir.display()
)));
} }
// Parse the manifest file // Parse the manifest file
@ -416,14 +418,26 @@ mod tests {
// Check if the file was stored in the correct directory structure // Check if the file was stored in the correct directory structure
let first_two = &hash[0..2]; let first_two = &hash[0..2];
let next_two = &hash[2..4]; let next_two = &hash[2..4];
let expected_path = repo_path.join("file").join(first_two).join(next_two).join(&hash); let expected_path = repo_path
.join("file")
.join(first_two)
.join(next_two)
.join(&hash);
// Verify that the file exists at the expected path // Verify that the file exists at the expected path
assert!(expected_path.exists(), "File was not stored at the expected path: {}", expected_path.display()); assert!(
expected_path.exists(),
"File was not stored at the expected path: {}",
expected_path.display()
);
// Verify that the file does NOT exist at the old path // Verify that the file does NOT exist at the old path
let old_path = repo_path.join("file").join(&hash); let old_path = repo_path.join("file").join(&hash);
assert!(!old_path.exists(), "File was stored at the old path: {}", old_path.display()); assert!(
!old_path.exists(),
"File was stored at the old path: {}",
old_path.display()
);
// Clean up // Clean up
cleanup_test_dir(&test_dir); cleanup_test_dir(&test_dir);

View file

@ -33,7 +33,10 @@ mod tests {
// Verify that the parsed manifest matches the original // Verify that the parsed manifest matches the original
assert_eq!(parsed_manifest.attributes.len(), 1); assert_eq!(parsed_manifest.attributes.len(), 1);
assert_eq!(parsed_manifest.attributes[0].key, "pkg.fmri"); assert_eq!(parsed_manifest.attributes[0].key, "pkg.fmri");
assert_eq!(parsed_manifest.attributes[0].values[0], "pkg://test/example@1.0.0"); assert_eq!(
parsed_manifest.attributes[0].values[0],
"pkg://test/example@1.0.0"
);
} }
#[test] #[test]
@ -55,6 +58,9 @@ mod tests {
// Verify that the parsed manifest has the expected attributes // Verify that the parsed manifest has the expected attributes
assert_eq!(parsed_manifest.attributes.len(), 1); assert_eq!(parsed_manifest.attributes.len(), 1);
assert_eq!(parsed_manifest.attributes[0].key, "pkg.fmri"); assert_eq!(parsed_manifest.attributes[0].key, "pkg.fmri");
assert_eq!(parsed_manifest.attributes[0].values[0], "pkg://test/example@1.0.0"); assert_eq!(
parsed_manifest.attributes[0].values[0],
"pkg://test/example@1.0.0"
);
} }
} }

View file

@ -17,23 +17,44 @@ fn test_parse_postgre_common_manifest() {
// Check the group action // Check the group action
let group = &manifest.groups[0]; let group = &manifest.groups[0];
assert_eq!(group.groupname, "postgres", "Expected groupname to be 'postgres'"); assert_eq!(
group.groupname, "postgres",
"Expected groupname to be 'postgres'"
);
assert_eq!(group.gid, "90", "Expected gid to be '90'"); assert_eq!(group.gid, "90", "Expected gid to be '90'");
// Check the user action // Check the user action
let user = &manifest.users[0]; let user = &manifest.users[0];
assert_eq!(user.username, "postgres", "Expected username to be 'postgres'"); assert_eq!(
user.username, "postgres",
"Expected username to be 'postgres'"
);
assert_eq!(user.uid, "90", "Expected uid to be '90'"); assert_eq!(user.uid, "90", "Expected uid to be '90'");
assert_eq!(user.group, "postgres", "Expected group to be 'postgres'"); assert_eq!(user.group, "postgres", "Expected group to be 'postgres'");
assert_eq!(user.home_dir, "/var/postgres", "Expected home_dir to be '/var/postgres'"); assert_eq!(
assert_eq!(user.login_shell, "/usr/bin/pfksh", "Expected login_shell to be '/usr/bin/pfksh'"); user.home_dir, "/var/postgres",
"Expected home_dir to be '/var/postgres'"
);
assert_eq!(
user.login_shell, "/usr/bin/pfksh",
"Expected login_shell to be '/usr/bin/pfksh'"
);
assert_eq!(user.password, "NP", "Expected password to be 'NP'"); assert_eq!(user.password, "NP", "Expected password to be 'NP'");
assert!(user.services.is_empty(), "Expected no services for ftpuser=false"); assert!(
assert_eq!(user.gcos_field, "PostgreSQL Reserved UID", "Expected gcos_field to be 'PostgreSQL Reserved UID'"); user.services.is_empty(),
"Expected no services for ftpuser=false"
);
assert_eq!(
user.gcos_field, "PostgreSQL Reserved UID",
"Expected gcos_field to be 'PostgreSQL Reserved UID'"
);
// Check the directory action // Check the directory action
let dir = &manifest.directories[0]; let dir = &manifest.directories[0];
assert_eq!(dir.path, "var/postgres", "Expected path to be 'var/postgres'"); assert_eq!(
dir.path, "var/postgres",
"Expected path to be 'var/postgres'"
);
assert_eq!(dir.group, "postgres", "Expected group to be 'postgres'"); assert_eq!(dir.group, "postgres", "Expected group to be 'postgres'");
assert_eq!(dir.owner, "postgres", "Expected owner to be 'postgres'"); assert_eq!(dir.owner, "postgres", "Expected owner to be 'postgres'");
assert_eq!(dir.mode, "0755", "Expected mode to be '0755'"); assert_eq!(dir.mode, "0755", "Expected mode to be '0755'");
@ -52,8 +73,20 @@ fn test_parse_pgadmin_manifest() {
// Check the legacy action // Check the legacy action
let legacy = &manifest.legacies[0]; let legacy = &manifest.legacies[0];
assert_eq!(legacy.arch, "i386", "Expected arch to be 'i386'"); assert_eq!(legacy.arch, "i386", "Expected arch to be 'i386'");
assert_eq!(legacy.category, "system", "Expected category to be 'system'"); assert_eq!(
assert_eq!(legacy.pkg, "SUNWpgadmin3", "Expected pkg to be 'SUNWpgadmin3'"); legacy.category, "system",
assert_eq!(legacy.vendor, "Project OpenIndiana", "Expected vendor to be 'Project OpenIndiana'"); "Expected category to be 'system'"
assert_eq!(legacy.version, "11.11.0,REV=2010.05.25.01.00", "Expected version to be '11.11.0,REV=2010.05.25.01.00'"); );
assert_eq!(
legacy.pkg, "SUNWpgadmin3",
"Expected pkg to be 'SUNWpgadmin3'"
);
assert_eq!(
legacy.vendor, "Project OpenIndiana",
"Expected vendor to be 'Project OpenIndiana'"
);
assert_eq!(
legacy.version, "11.11.0,REV=2010.05.25.01.00",
"Expected version to be '11.11.0,REV=2010.05.25.01.00'"
);
} }

View file

@ -14,7 +14,10 @@ fn test_ftpuser_boolean_true() {
let user = &manifest.users[0]; let user = &manifest.users[0];
// Check that "ftp" service is added // Check that "ftp" service is added
assert!(user.services.contains("ftp"), "Expected 'ftp' service to be added when ftpuser=true"); assert!(
user.services.contains("ftp"),
"Expected 'ftp' service to be added when ftpuser=true"
);
assert_eq!(user.services.len(), 1, "Expected exactly one service"); assert_eq!(user.services.len(), 1, "Expected exactly one service");
} }
@ -30,7 +33,10 @@ fn test_ftpuser_boolean_false() {
let user = &manifest.users[0]; let user = &manifest.users[0];
// Check that no services are added // Check that no services are added
assert!(user.services.is_empty(), "Expected no services when ftpuser=false"); assert!(
user.services.is_empty(),
"Expected no services when ftpuser=false"
);
} }
#[test] #[test]
@ -45,9 +51,18 @@ fn test_ftpuser_services_list() {
let user = &manifest.users[0]; let user = &manifest.users[0];
// Check that all services are added // Check that all services are added
assert!(user.services.contains("ssh"), "Expected 'ssh' service to be added"); assert!(
assert!(user.services.contains("ftp"), "Expected 'ftp' service to be added"); user.services.contains("ssh"),
assert!(user.services.contains("http"), "Expected 'http' service to be added"); "Expected 'ssh' service to be added"
);
assert!(
user.services.contains("ftp"),
"Expected 'ftp' service to be added"
);
assert!(
user.services.contains("http"),
"Expected 'http' service to be added"
);
assert_eq!(user.services.len(), 3, "Expected exactly three services"); assert_eq!(user.services.len(), 3, "Expected exactly three services");
} }
@ -63,9 +78,18 @@ fn test_ftpuser_services_with_whitespace() {
let user = &manifest.users[0]; let user = &manifest.users[0];
// Check that all services are added with whitespace trimmed // Check that all services are added with whitespace trimmed
assert!(user.services.contains("ssh"), "Expected 'ssh' service to be added"); assert!(
assert!(user.services.contains("ftp"), "Expected 'ftp' service to be added"); user.services.contains("ssh"),
assert!(user.services.contains("http"), "Expected 'http' service to be added"); "Expected 'ssh' service to be added"
);
assert!(
user.services.contains("ftp"),
"Expected 'ftp' service to be added"
);
assert!(
user.services.contains("http"),
"Expected 'http' service to be added"
);
assert_eq!(user.services.len(), 3, "Expected exactly three services"); assert_eq!(user.services.len(), 3, "Expected exactly three services");
} }
@ -81,7 +105,10 @@ fn test_ftpuser_empty_string() {
let user = &manifest.users[0]; let user = &manifest.users[0];
// Check that no services are added // Check that no services are added
assert!(user.services.is_empty(), "Expected no services for empty string"); assert!(
user.services.is_empty(),
"Expected no services for empty string"
);
} }
#[test] #[test]
@ -96,8 +123,14 @@ fn test_ftpuser_with_empty_elements() {
let user = &manifest.users[0]; let user = &manifest.users[0];
// Check that only non-empty services are added // Check that only non-empty services are added
assert!(user.services.contains("ssh"), "Expected 'ssh' service to be added"); assert!(
assert!(user.services.contains("http"), "Expected 'http' service to be added"); user.services.contains("ssh"),
"Expected 'ssh' service to be added"
);
assert!(
user.services.contains("http"),
"Expected 'http' service to be added"
);
assert_eq!(user.services.len(), 2, "Expected exactly two services"); assert_eq!(user.services.len(), 2, "Expected exactly two services");
} }
@ -120,5 +153,8 @@ fn test_real_world_example() {
assert_eq!(user.login_shell, "/usr/bin/pfksh"); assert_eq!(user.login_shell, "/usr/bin/pfksh");
assert_eq!(user.password, "NP"); assert_eq!(user.password, "NP");
assert_eq!(user.gcos_field, "PostgreSQL Reserved UID"); assert_eq!(user.gcos_field, "PostgreSQL Reserved UID");
assert!(user.services.is_empty(), "Expected no services for ftpuser=false"); assert!(
user.services.is_empty(),
"Expected no services for ftpuser=false"
);
} }

View file

@ -41,27 +41,21 @@ pub enum Pkg6DevError {
code(ips::pkg6dev::component_path_error), code(ips::pkg6dev::component_path_error),
help("Ensure the component path exists and is a directory") help("Ensure the component path exists and is a directory")
)] )]
ComponentPathError { ComponentPathError { path: PathBuf },
path: PathBuf,
},
#[error("manifest not found: {path}")] #[error("manifest not found: {path}")]
#[diagnostic( #[diagnostic(
code(ips::pkg6dev::manifest_not_found), code(ips::pkg6dev::manifest_not_found),
help("Ensure the manifest file exists at the specified path") help("Ensure the manifest file exists at the specified path")
)] )]
ManifestNotFoundError { ManifestNotFoundError { path: PathBuf },
path: PathBuf,
},
#[error("replacement format error: {value} is not in the format 'key:value'")] #[error("replacement format error: {value} is not in the format 'key:value'")]
#[diagnostic( #[diagnostic(
code(ips::pkg6dev::replacement_format_error), code(ips::pkg6dev::replacement_format_error),
help("Replacements must be in the format 'key:value'") help("Replacements must be in the format 'key:value'")
)] )]
ReplacementFormatError { ReplacementFormatError { value: String },
value: String,
},
// Makefile-related errors // Makefile-related errors
#[error("makefile parse error: {message}")] #[error("makefile parse error: {message}")]
@ -69,18 +63,14 @@ pub enum Pkg6DevError {
code(ips::pkg6dev::makefile_parse_error), code(ips::pkg6dev::makefile_parse_error),
help("Check the Makefile syntax and try again") help("Check the Makefile syntax and try again")
)] )]
MakefileParseError { MakefileParseError { message: String },
message: String,
},
#[error("component info error: {message}")] #[error("component info error: {message}")]
#[diagnostic( #[diagnostic(
code(ips::pkg6dev::component_info_error), code(ips::pkg6dev::component_info_error),
help("Check the component information and try again") help("Check the component information and try again")
)] )]
ComponentInfoError { ComponentInfoError { message: String },
message: String,
},
// Package publishing errors // Package publishing errors
#[error("manifest file not found: {path}")] #[error("manifest file not found: {path}")]
@ -88,27 +78,21 @@ pub enum Pkg6DevError {
code(ips::pkg6dev::manifest_file_not_found), code(ips::pkg6dev::manifest_file_not_found),
help("Ensure the manifest file exists at the specified path") help("Ensure the manifest file exists at the specified path")
)] )]
ManifestFileNotFoundError { ManifestFileNotFoundError { path: PathBuf },
path: PathBuf,
},
#[error("prototype directory not found: {path}")] #[error("prototype directory not found: {path}")]
#[diagnostic( #[diagnostic(
code(ips::pkg6dev::prototype_dir_not_found), code(ips::pkg6dev::prototype_dir_not_found),
help("Ensure the prototype directory exists at the specified path") help("Ensure the prototype directory exists at the specified path")
)] )]
PrototypeDirNotFoundError { PrototypeDirNotFoundError { path: PathBuf },
path: PathBuf,
},
#[error("publisher not found: {publisher}")] #[error("publisher not found: {publisher}")]
#[diagnostic( #[diagnostic(
code(ips::pkg6dev::publisher_not_found), code(ips::pkg6dev::publisher_not_found),
help("Add the publisher to the repository using pkg6repo add-publisher") help("Add the publisher to the repository using pkg6repo add-publisher")
)] )]
PublisherNotFoundError { PublisherNotFoundError { publisher: String },
publisher: String,
},
#[error("no default publisher set")] #[error("no default publisher set")]
#[diagnostic( #[diagnostic(
@ -122,9 +106,7 @@ pub enum Pkg6DevError {
code(ips::pkg6dev::file_not_found_in_prototype), code(ips::pkg6dev::file_not_found_in_prototype),
help("Ensure the file exists in the prototype directory") help("Ensure the file exists in the prototype directory")
)] )]
FileNotFoundInPrototypeError { FileNotFoundInPrototypeError { path: PathBuf },
path: PathBuf,
},
// Logging environment error // Logging environment error
#[error("logging environment setup error: {0}")] #[error("logging environment setup error: {0}")]

View file

@ -6,12 +6,12 @@ use libips::repository::{FileBackend, ReadableRepository, WritableRepository};
use error::{Pkg6DevError, Result}; use error::{Pkg6DevError, Result};
use std::collections::HashMap; use std::collections::HashMap;
use std::fs::{read_dir, OpenOptions}; use std::fs::{OpenOptions, read_dir};
use std::io::Write; use std::io::Write;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use tracing::{debug, info, warn}; use tracing::{debug, info, warn};
use tracing_subscriber::{fmt, EnvFilter};
use tracing_subscriber::filter::LevelFilter; use tracing_subscriber::filter::LevelFilter;
use tracing_subscriber::{EnvFilter, fmt};
use userland::repology::find_newest_version; use userland::repology::find_newest_version;
use userland::{Component, Makefile}; use userland::{Component, Makefile};
@ -63,7 +63,9 @@ fn main() -> Result<()> {
let env_filter = EnvFilter::builder() let env_filter = EnvFilter::builder()
.with_default_directive(LevelFilter::WARN.into()) .with_default_directive(LevelFilter::WARN.into())
.from_env() .from_env()
.map_err(|e| Pkg6DevError::LoggingEnvError(format!("Failed to parse environment filter: {}", e)))?; .map_err(|e| {
Pkg6DevError::LoggingEnvError(format!("Failed to parse environment filter: {}", e))
})?;
fmt::Subscriber::builder() fmt::Subscriber::builder()
.with_max_level(tracing::Level::DEBUG) .with_max_level(tracing::Level::DEBUG)
@ -200,7 +202,8 @@ fn diff_component(
.write(true) .write(true)
.truncate(true) .truncate(true)
.create(true) .create(true)
.open(output_manifest).map_err(|e| Pkg6DevError::IoError(e))?; .open(output_manifest)
.map_err(|e| Pkg6DevError::IoError(e))?;
for action in missing_files { for action in missing_files {
writeln!(&mut f, "file path={}", action.path).map_err(|e| Pkg6DevError::IoError(e))?; writeln!(&mut f, "file path={}", action.path).map_err(|e| Pkg6DevError::IoError(e))?;
} }
@ -228,12 +231,14 @@ fn show_component_info<P: AsRef<Path>>(component_path: P) -> Result<()> {
// Parse Makefile // Parse Makefile
// We'll wrap the anyhow errors with our more specific error types // We'll wrap the anyhow errors with our more specific error types
let initial_makefile = Makefile::parse_single_file(&makefile_path) let initial_makefile = Makefile::parse_single_file(&makefile_path).map_err(|e| {
.map_err(|e| Pkg6DevError::MakefileParseError { Pkg6DevError::MakefileParseError {
message: format!("Failed to parse Makefile: {}", e), message: format!("Failed to parse Makefile: {}", e),
}
})?; })?;
let makefile = initial_makefile.parse_all() let makefile = initial_makefile
.parse_all()
.map_err(|e| Pkg6DevError::MakefileParseError { .map_err(|e| Pkg6DevError::MakefileParseError {
message: format!("Failed to parse all Makefiles: {}", e), message: format!("Failed to parse all Makefiles: {}", e),
})?; })?;
@ -241,8 +246,8 @@ fn show_component_info<P: AsRef<Path>>(component_path: P) -> Result<()> {
let mut name = String::new(); let mut name = String::new();
// Get component information // Get component information
let component = Component::new_from_makefile(&makefile) let component =
.map_err(|e| Pkg6DevError::ComponentInfoError { Component::new_from_makefile(&makefile).map_err(|e| Pkg6DevError::ComponentInfoError {
message: format!("Failed to get component information: {}", e), message: format!("Failed to get component information: {}", e),
})?; })?;
@ -259,9 +264,12 @@ fn show_component_info<P: AsRef<Path>>(component_path: P) -> Result<()> {
info!("Version: {}", var.replace('\n', "\n\t")); info!("Version: {}", var.replace('\n', "\n\t"));
let latest_version = find_newest_version(&name); let latest_version = find_newest_version(&name);
if latest_version.is_ok() { if latest_version.is_ok() {
info!("Latest Version: {}", latest_version.map_err(|e| Pkg6DevError::ComponentInfoError { info!(
"Latest Version: {}",
latest_version.map_err(|e| Pkg6DevError::ComponentInfoError {
message: format!("Failed to get latest version: {}", e), message: format!("Failed to get latest version: {}", e),
})?); })?
);
} else { } else {
warn!( warn!(
"Could not get latest version info: {}", "Could not get latest version info: {}",
@ -448,7 +456,7 @@ fn publish_package(
// Use the default publisher // Use the default publisher
match &repo.config.default_publisher { match &repo.config.default_publisher {
Some(default_pub) => default_pub.clone(), Some(default_pub) => default_pub.clone(),
None => return Err(Pkg6DevError::NoDefaultPublisherError) None => return Err(Pkg6DevError::NoDefaultPublisherError),
} }
}; };
@ -470,7 +478,9 @@ fn publish_package(
// Check if the file exists // Check if the file exists
if !file_path.exists() { if !file_path.exists() {
return Err(Pkg6DevError::FileNotFoundInPrototypeError{path: file_path.clone()}) return Err(Pkg6DevError::FileNotFoundInPrototypeError {
path: file_path.clone(),
});
} }
// Add the file to the transaction // Add the file to the transaction

View file

@ -161,7 +161,12 @@ mod e2e_tests {
); );
// Add a publisher using pkg6repo // Add a publisher using pkg6repo
let result = run_pkg6repo(&["add-publisher", "-s", repo_path.to_str().unwrap(), "example.com"]); let result = run_pkg6repo(&[
"add-publisher",
"-s",
repo_path.to_str().unwrap(),
"example.com",
]);
assert!( assert!(
result.is_ok(), result.is_ok(),
"Failed to add publisher: {:?}", "Failed to add publisher: {:?}",
@ -205,9 +210,12 @@ mod e2e_tests {
let manifest_path = manifest_dir.join("example.p5m"); let manifest_path = manifest_dir.join("example.p5m");
let result = run_pkg6dev(&[ let result = run_pkg6dev(&[
"publish", "publish",
"--manifest-path", manifest_path.to_str().unwrap(), "--manifest-path",
"--prototype-dir", prototype_dir.to_str().unwrap(), manifest_path.to_str().unwrap(),
"--repo-path", repo_path.to_str().unwrap(), "--prototype-dir",
prototype_dir.to_str().unwrap(),
"--repo-path",
repo_path.to_str().unwrap(),
]); ]);
assert!( assert!(
result.is_ok(), result.is_ok(),
@ -262,9 +270,12 @@ mod e2e_tests {
let manifest_path = manifest_dir.join("example.p5m"); let manifest_path = manifest_dir.join("example.p5m");
let result = run_pkg6dev(&[ let result = run_pkg6dev(&[
"publish", "publish",
"--manifest-path", manifest_path.to_str().unwrap(), "--manifest-path",
"--prototype-dir", prototype_dir.to_str().unwrap(), manifest_path.to_str().unwrap(),
"--repo-path", repo_path.to_str().unwrap(), "--prototype-dir",
prototype_dir.to_str().unwrap(),
"--repo-path",
repo_path.to_str().unwrap(),
]); ]);
assert!( assert!(
result.is_ok(), result.is_ok(),
@ -327,9 +338,12 @@ mod e2e_tests {
let manifest_path1 = manifest_dir.join("example.p5m"); let manifest_path1 = manifest_dir.join("example.p5m");
let result = run_pkg6dev(&[ let result = run_pkg6dev(&[
"publish", "publish",
"--manifest-path", manifest_path1.to_str().unwrap(), "--manifest-path",
"--prototype-dir", prototype_dir.to_str().unwrap(), manifest_path1.to_str().unwrap(),
"--repo-path", repo_path.to_str().unwrap(), "--prototype-dir",
prototype_dir.to_str().unwrap(),
"--repo-path",
repo_path.to_str().unwrap(),
]); ]);
assert!( assert!(
result.is_ok(), result.is_ok(),
@ -341,9 +355,12 @@ mod e2e_tests {
let manifest_path2 = manifest_dir.join("example2.p5m"); let manifest_path2 = manifest_dir.join("example2.p5m");
let result = run_pkg6dev(&[ let result = run_pkg6dev(&[
"publish", "publish",
"--manifest-path", manifest_path2.to_str().unwrap(), "--manifest-path",
"--prototype-dir", prototype_dir.to_str().unwrap(), manifest_path2.to_str().unwrap(),
"--repo-path", repo_path.to_str().unwrap(), "--prototype-dir",
prototype_dir.to_str().unwrap(),
"--repo-path",
repo_path.to_str().unwrap(),
]); ]);
assert!( assert!(
result.is_ok(), result.is_ok(),
@ -382,7 +399,10 @@ mod e2e_tests {
// Check if the sample repository exists // Check if the sample repository exists
if !sample_repo_path.exists() { if !sample_repo_path.exists() {
println!("Sample pkg5 repository not found at {}, skipping test", sample_repo_path.display()); println!(
"Sample pkg5 repository not found at {}, skipping test",
sample_repo_path.display()
);
return; return;
} }
@ -393,8 +413,10 @@ mod e2e_tests {
// Import the pkg5 repository using pkg6repo // Import the pkg5 repository using pkg6repo
let result = run_pkg6repo(&[ let result = run_pkg6repo(&[
"import-pkg5", "import-pkg5",
"--source", sample_repo_path.to_str().unwrap(), "--source",
"--destination", repo_path.to_str().unwrap(), sample_repo_path.to_str().unwrap(),
"--destination",
repo_path.to_str().unwrap(),
]); ]);
assert!( assert!(
result.is_ok(), result.is_ok(),
@ -422,10 +444,7 @@ mod e2e_tests {
); );
let output = result.unwrap(); let output = result.unwrap();
assert!( assert!(!output.is_empty(), "No packages found in repository");
!output.is_empty(),
"No packages found in repository"
);
// Clean up // Clean up
cleanup_test_dir(&test_dir); cleanup_test_dir(&test_dir);
@ -440,7 +459,10 @@ mod e2e_tests {
// Check if the sample p5p archive exists // Check if the sample p5p archive exists
if !sample_p5p_path.exists() { if !sample_p5p_path.exists() {
println!("Sample pkg5 p5p archive not found at {}, skipping test", sample_p5p_path.display()); println!(
"Sample pkg5 p5p archive not found at {}, skipping test",
sample_p5p_path.display()
);
return; return;
} }
@ -451,8 +473,10 @@ mod e2e_tests {
// Import the pkg5 p5p archive using pkg6repo // Import the pkg5 p5p archive using pkg6repo
let result = run_pkg6repo(&[ let result = run_pkg6repo(&[
"import-pkg5", "import-pkg5",
"--source", sample_p5p_path.to_str().unwrap(), "--source",
"--destination", repo_path.to_str().unwrap(), sample_p5p_path.to_str().unwrap(),
"--destination",
repo_path.to_str().unwrap(),
]); ]);
assert!( assert!(
result.is_ok(), result.is_ok(),
@ -480,10 +504,7 @@ mod e2e_tests {
); );
let output = result.unwrap(); let output = result.unwrap();
assert!( assert!(!output.is_empty(), "No packages found in repository");
!output.is_empty(),
"No packages found in repository"
);
// Clean up // Clean up
cleanup_test_dir(&test_dir); cleanup_test_dir(&test_dir);

View file

@ -56,10 +56,7 @@ pub enum Pkg6RepoError {
LoggingEnvError(String), LoggingEnvError(String),
#[error("other error: {0}")] #[error("other error: {0}")]
#[diagnostic( #[diagnostic(code(pkg6repo::other_error), help("See error message for details"))]
code(pkg6repo::other_error),
help("See error message for details")
)]
Other(String), Other(String),
} }

View file

@ -4,13 +4,13 @@ use error::{Pkg6RepoError, Result};
use pkg5_import::Pkg5Importer; use pkg5_import::Pkg5Importer;
use clap::{Parser, Subcommand}; use clap::{Parser, Subcommand};
use libips::repository::{FileBackend, ReadableRepository, RepositoryVersion, WritableRepository};
use serde::Serialize; use serde::Serialize;
use std::convert::TryFrom; use std::convert::TryFrom;
use std::path::PathBuf; use std::path::PathBuf;
use tracing::{debug, info}; use tracing::{debug, info};
use tracing_subscriber::{fmt, EnvFilter};
use tracing_subscriber::filter::LevelFilter; use tracing_subscriber::filter::LevelFilter;
use libips::repository::{FileBackend, ReadableRepository, RepositoryVersion, WritableRepository}; use tracing_subscriber::{EnvFilter, fmt};
#[cfg(test)] #[cfg(test)]
mod e2e_tests; mod e2e_tests;
@ -326,7 +326,9 @@ fn main() -> Result<()> {
let env_filter = EnvFilter::builder() let env_filter = EnvFilter::builder()
.with_default_directive(LevelFilter::WARN.into()) .with_default_directive(LevelFilter::WARN.into())
.from_env() .from_env()
.map_err(|e| Pkg6RepoError::LoggingEnvError(format!("Failed to parse environment filter: {}", e)))?; .map_err(|e| {
Pkg6RepoError::LoggingEnvError(format!("Failed to parse environment filter: {}", e))
})?;
fmt::Subscriber::builder() fmt::Subscriber::builder()
.with_max_level(tracing::Level::DEBUG) .with_max_level(tracing::Level::DEBUG)
@ -525,7 +527,9 @@ fn main() -> Result<()> {
} }
} }
_ => { _ => {
return Err(Pkg6RepoError::UnsupportedOutputFormat(output_format.to_string())); return Err(Pkg6RepoError::UnsupportedOutputFormat(
output_format.to_string(),
));
} }
} }
@ -618,7 +622,9 @@ fn main() -> Result<()> {
} }
} }
_ => { _ => {
return Err(Pkg6RepoError::UnsupportedOutputFormat(output_format.to_string())); return Err(Pkg6RepoError::UnsupportedOutputFormat(
output_format.to_string(),
));
} }
} }
@ -727,7 +733,9 @@ fn main() -> Result<()> {
} }
} }
_ => { _ => {
return Err(Pkg6RepoError::UnsupportedOutputFormat(output_format.to_string())); return Err(Pkg6RepoError::UnsupportedOutputFormat(
output_format.to_string(),
));
} }
} }
@ -933,7 +941,9 @@ fn main() -> Result<()> {
// Split the property=value string // Split the property=value string
let parts: Vec<&str> = prop_val.split('=').collect(); let parts: Vec<&str> = prop_val.split('=').collect();
if parts.len() != 2 { if parts.len() != 2 {
return Err(Pkg6RepoError::InvalidPropertyValueFormat(prop_val.to_string())); return Err(Pkg6RepoError::InvalidPropertyValueFormat(
prop_val.to_string(),
));
} }
let property = parts[0]; let property = parts[0];
@ -1052,7 +1062,9 @@ fn main() -> Result<()> {
} }
} }
_ => { _ => {
return Err(Pkg6RepoError::UnsupportedOutputFormat(output_format.to_string())); return Err(Pkg6RepoError::UnsupportedOutputFormat(
output_format.to_string(),
));
} }
} }
@ -1063,7 +1075,11 @@ fn main() -> Result<()> {
destination, destination,
publisher, publisher,
} => { } => {
info!("Importing pkg5 repository from {} to {}", source.display(), destination.display()); info!(
"Importing pkg5 repository from {} to {}",
source.display(),
destination.display()
);
// Create a new Pkg5Importer // Create a new Pkg5Importer
let mut importer = Pkg5Importer::new(source, destination)?; let mut importer = Pkg5Importer::new(source, destination)?;

View file

@ -1,5 +1,5 @@
use crate::error::{Pkg6RepoError, Result}; use crate::error::{Pkg6RepoError, Result};
use libips::actions::{Manifest}; use libips::actions::Manifest;
use libips::repository::{FileBackend, ReadableRepository, WritableRepository}; use libips::repository::{FileBackend, ReadableRepository, WritableRepository};
use std::fs::{self, File}; use std::fs::{self, File};
use std::io::{Read, Seek}; use std::io::{Read, Seek};
@ -25,7 +25,11 @@ impl Pkg5Importer {
let source_path = source_path.as_ref().to_path_buf(); let source_path = source_path.as_ref().to_path_buf();
let dest_path = dest_path.as_ref().to_path_buf(); let dest_path = dest_path.as_ref().to_path_buf();
debug!("Creating Pkg5Importer with source: {}, destination: {}", source_path.display(), dest_path.display()); debug!(
"Creating Pkg5Importer with source: {}, destination: {}",
source_path.display(),
dest_path.display()
);
// Check if a source path exists // Check if a source path exists
if !source_path.exists() { if !source_path.exists() {
@ -38,7 +42,8 @@ impl Pkg5Importer {
debug!("Source path exists: {}", source_path.display()); debug!("Source path exists: {}", source_path.display());
// Determine if a source is a p5p archive // Determine if a source is a p5p archive
let is_p5p = source_path.is_file() && source_path.extension().map_or(false, |ext| ext == "p5p"); let is_p5p =
source_path.is_file() && source_path.extension().map_or(false, |ext| ext == "p5p");
debug!("Source is p5p archive: {}", is_p5p); debug!("Source is p5p archive: {}", is_p5p);
Ok(Self { Ok(Self {
@ -57,7 +62,10 @@ impl Pkg5Importer {
Pkg6RepoError::from(format!("Failed to create temporary directory: {}", e)) Pkg6RepoError::from(format!("Failed to create temporary directory: {}", e))
})?; })?;
info!("Extracting p5p archive to temporary directory: {}", temp_dir.path().display()); info!(
"Extracting p5p archive to temporary directory: {}",
temp_dir.path().display()
);
// Extract the p5p archive to the temporary directory // Extract the p5p archive to the temporary directory
let status = std::process::Command::new("tar") let status = std::process::Command::new("tar")
@ -101,11 +109,20 @@ impl Pkg5Importer {
let pkg5_repo_file = source_path.join("pkg5.repository"); let pkg5_repo_file = source_path.join("pkg5.repository");
let pkg5_index_file = source_path.join("pkg5.index.0.gz"); let pkg5_index_file = source_path.join("pkg5.index.0.gz");
debug!("Checking if pkg5.repository exists: {}", pkg5_repo_file.exists()); debug!(
debug!("Checking if pkg5.index.0.gz exists: {}", pkg5_index_file.exists()); "Checking if pkg5.repository exists: {}",
pkg5_repo_file.exists()
);
debug!(
"Checking if pkg5.index.0.gz exists: {}",
pkg5_index_file.exists()
);
if !pkg5_repo_file.exists() && !pkg5_index_file.exists() { if !pkg5_repo_file.exists() && !pkg5_index_file.exists() {
debug!("Source does not appear to be a pkg5 repository: {}", source_path.display()); debug!(
"Source does not appear to be a pkg5 repository: {}",
source_path.display()
);
return Err(Pkg6RepoError::from(format!( return Err(Pkg6RepoError::from(format!(
"Source does not appear to be a pkg5 repository: {}", "Source does not appear to be a pkg5 repository: {}",
source_path.display() source_path.display()
@ -113,27 +130,45 @@ impl Pkg5Importer {
} }
// Open or create the destination repository // Open or create the destination repository
debug!("Checking if destination repository exists: {}", self.dest_path.exists()); debug!(
"Checking if destination repository exists: {}",
self.dest_path.exists()
);
let mut dest_repo = if self.dest_path.exists() { let mut dest_repo = if self.dest_path.exists() {
// Check if it's a valid repository by looking for the pkg6.repository file // Check if it's a valid repository by looking for the pkg6.repository file
let repo_config_file = self.dest_path.join("pkg6.repository"); let repo_config_file = self.dest_path.join("pkg6.repository");
debug!("Checking if repository config file exists: {}", repo_config_file.exists()); debug!(
"Checking if repository config file exists: {}",
repo_config_file.exists()
);
if repo_config_file.exists() { if repo_config_file.exists() {
// It's a valid repository, open it // It's a valid repository, open it
info!("Opening existing repository: {}", self.dest_path.display()); info!("Opening existing repository: {}", self.dest_path.display());
debug!("Attempting to open repository at: {}", self.dest_path.display()); debug!(
"Attempting to open repository at: {}",
self.dest_path.display()
);
FileBackend::open(&self.dest_path)? FileBackend::open(&self.dest_path)?
} else { } else {
// It's not a valid repository, create a new one // It's not a valid repository, create a new one
info!("Destination exists but is not a valid repository, creating a new one: {}", self.dest_path.display()); info!(
debug!("Attempting to create repository at: {}", self.dest_path.display()); "Destination exists but is not a valid repository, creating a new one: {}",
self.dest_path.display()
);
debug!(
"Attempting to create repository at: {}",
self.dest_path.display()
);
FileBackend::create(&self.dest_path, libips::repository::RepositoryVersion::V4)? FileBackend::create(&self.dest_path, libips::repository::RepositoryVersion::V4)?
} }
} else { } else {
// Destination doesn't exist, create a new repository // Destination doesn't exist, create a new repository
info!("Creating new repository: {}", self.dest_path.display()); info!("Creating new repository: {}", self.dest_path.display());
debug!("Attempting to create repository at: {}", self.dest_path.display()); debug!(
"Attempting to create repository at: {}",
self.dest_path.display()
);
FileBackend::create(&self.dest_path, libips::repository::RepositoryVersion::V4)? FileBackend::create(&self.dest_path, libips::repository::RepositoryVersion::V4)?
}; };
@ -142,7 +177,7 @@ impl Pkg5Importer {
if publishers.is_empty() { if publishers.is_empty() {
return Err(Pkg6RepoError::from( return Err(Pkg6RepoError::from(
"No publishers found in source repository".to_string() "No publishers found in source repository".to_string(),
)); ));
} }
@ -156,7 +191,7 @@ impl Pkg5Importer {
))); )));
} }
pub_name pub_name
}, }
None => { None => {
// Use the first publisher if none specified // Use the first publisher if none specified
&publishers[0] &publishers[0]
@ -166,8 +201,16 @@ impl Pkg5Importer {
info!("Importing from publisher: {}", publisher_to_import); info!("Importing from publisher: {}", publisher_to_import);
// Ensure the publisher exists in the destination repository // Ensure the publisher exists in the destination repository
if !dest_repo.config.publishers.iter().any(|p| p == publisher_to_import) { if !dest_repo
info!("Adding publisher to destination repository: {}", publisher_to_import); .config
.publishers
.iter()
.any(|p| p == publisher_to_import)
{
info!(
"Adding publisher to destination repository: {}",
publisher_to_import
);
dest_repo.add_publisher(publisher_to_import)?; dest_repo.add_publisher(publisher_to_import)?;
// Set as the default publisher if there isn't one already // Set as the default publisher if there isn't one already
@ -201,12 +244,8 @@ impl Pkg5Importer {
let mut publishers = Vec::new(); let mut publishers = Vec::new();
for entry in fs::read_dir(&publisher_dir).map_err(|e| { for entry in fs::read_dir(&publisher_dir).map_err(|e| Pkg6RepoError::IoError(e))? {
Pkg6RepoError::IoError(e) let entry = entry.map_err(|e| Pkg6RepoError::IoError(e))?;
})? {
let entry = entry.map_err(|e| {
Pkg6RepoError::IoError(e)
})?;
let path = entry.path(); let path = entry.path();
@ -220,7 +259,12 @@ impl Pkg5Importer {
} }
/// Imports packages from the source repository /// Imports packages from the source repository
fn import_packages(&self, source_path: &Path, dest_repo: &mut FileBackend, publisher: &str) -> Result<()> { fn import_packages(
&self,
source_path: &Path,
dest_repo: &mut FileBackend,
publisher: &str,
) -> Result<()> {
let pkg_dir = source_path.join("publisher").join(publisher).join("pkg"); let pkg_dir = source_path.join("publisher").join(publisher).join("pkg");
if !pkg_dir.exists() || !pkg_dir.is_dir() { if !pkg_dir.exists() || !pkg_dir.is_dir() {
@ -232,20 +276,22 @@ impl Pkg5Importer {
// Create a temporary directory for extracted files // Create a temporary directory for extracted files
let temp_proto_dir = tempdir().map_err(|e| { let temp_proto_dir = tempdir().map_err(|e| {
Pkg6RepoError::from(format!("Failed to create temporary prototype directory: {}", e)) Pkg6RepoError::from(format!(
"Failed to create temporary prototype directory: {}",
e
))
})?; })?;
info!("Created temporary prototype directory: {}", temp_proto_dir.path().display()); info!(
"Created temporary prototype directory: {}",
temp_proto_dir.path().display()
);
// Find package directories // Find package directories
let mut package_count = 0; let mut package_count = 0;
for pkg_entry in fs::read_dir(&pkg_dir).map_err(|e| { for pkg_entry in fs::read_dir(&pkg_dir).map_err(|e| Pkg6RepoError::IoError(e))? {
Pkg6RepoError::IoError(e) let pkg_entry = pkg_entry.map_err(|e| Pkg6RepoError::IoError(e))?;
})? {
let pkg_entry = pkg_entry.map_err(|e| {
Pkg6RepoError::IoError(e)
})?;
let pkg_path = pkg_entry.path(); let pkg_path = pkg_entry.path();
@ -257,12 +303,8 @@ impl Pkg5Importer {
debug!("Processing package: {}", decoded_pkg_name); debug!("Processing package: {}", decoded_pkg_name);
// Find package versions // Find package versions
for ver_entry in fs::read_dir(&pkg_path).map_err(|e| { for ver_entry in fs::read_dir(&pkg_path).map_err(|e| Pkg6RepoError::IoError(e))? {
Pkg6RepoError::IoError(e) let ver_entry = ver_entry.map_err(|e| Pkg6RepoError::IoError(e))?;
})? {
let ver_entry = ver_entry.map_err(|e| {
Pkg6RepoError::IoError(e)
})?;
let ver_path = ver_entry.path(); let ver_path = ver_entry.path();
@ -311,7 +353,10 @@ impl Pkg5Importer {
debug!("Extracted package name from FMRI: {}", pkg_name); debug!("Extracted package name from FMRI: {}", pkg_name);
// Read the manifest file content // Read the manifest file content
debug!("Reading manifest file content from {}", manifest_path.display()); debug!(
"Reading manifest file content from {}",
manifest_path.display()
);
let manifest_content = fs::read_to_string(manifest_path).map_err(|e| { let manifest_content = fs::read_to_string(manifest_path).map_err(|e| {
debug!("Error reading manifest file: {}", e); debug!("Error reading manifest file: {}", e);
Pkg6RepoError::IoError(e) Pkg6RepoError::IoError(e)
@ -330,7 +375,10 @@ impl Pkg5Importer {
transaction.set_publisher(publisher); transaction.set_publisher(publisher);
// Debug the repository structure // Debug the repository structure
debug!("Publisher directory: {}", dest_repo.path.join("pkg").join(publisher).display()); debug!(
"Publisher directory: {}",
dest_repo.path.join("pkg").join(publisher).display()
);
// Extract files referenced in the manifest // Extract files referenced in the manifest
let file_dir = source_path.join("publisher").join(publisher).join("file"); let file_dir = source_path.join("publisher").join(publisher).join("file");
@ -353,7 +401,10 @@ impl Pkg5Importer {
let file_path = file_dir.join(hash_prefix).join(&hash); let file_path = file_dir.join(hash_prefix).join(&hash);
if !file_path.exists() { if !file_path.exists() {
warn!("File not found in source repository: {}", file_path.display()); warn!(
"File not found in source repository: {}",
file_path.display()
);
continue; continue;
} }
@ -362,42 +413,36 @@ impl Pkg5Importer {
// Create parent directories if they don't exist // Create parent directories if they don't exist
if let Some(parent) = proto_file_path.parent() { if let Some(parent) = proto_file_path.parent() {
fs::create_dir_all(parent).map_err(|e| { fs::create_dir_all(parent).map_err(|e| Pkg6RepoError::IoError(e))?;
Pkg6RepoError::IoError(e)
})?;
} }
// Extract the gzipped file // Extract the gzipped file
let mut source_file = File::open(&file_path).map_err(|e| { let mut source_file =
Pkg6RepoError::IoError(e) File::open(&file_path).map_err(|e| Pkg6RepoError::IoError(e))?;
})?;
let mut dest_file = File::create(&proto_file_path).map_err(|e| { let mut dest_file =
Pkg6RepoError::IoError(e) File::create(&proto_file_path).map_err(|e| Pkg6RepoError::IoError(e))?;
})?;
// Check if the file is gzipped // Check if the file is gzipped
let mut header = [0; 2]; let mut header = [0; 2];
source_file.read_exact(&mut header).map_err(|e| { source_file
Pkg6RepoError::IoError(e) .read_exact(&mut header)
})?; .map_err(|e| Pkg6RepoError::IoError(e))?;
// Reset file position // Reset file position
source_file.seek(std::io::SeekFrom::Start(0)).map_err(|e| { source_file
Pkg6RepoError::IoError(e) .seek(std::io::SeekFrom::Start(0))
})?; .map_err(|e| Pkg6RepoError::IoError(e))?;
if header[0] == 0x1f && header[1] == 0x8b { if header[0] == 0x1f && header[1] == 0x8b {
// File is gzipped, decompress it // File is gzipped, decompress it
let mut decoder = flate2::read::GzDecoder::new(source_file); let mut decoder = flate2::read::GzDecoder::new(source_file);
std::io::copy(&mut decoder, &mut dest_file).map_err(|e| { std::io::copy(&mut decoder, &mut dest_file)
Pkg6RepoError::IoError(e) .map_err(|e| Pkg6RepoError::IoError(e))?;
})?;
} else { } else {
// File is not gzipped, copy it as is // File is not gzipped, copy it as is
std::io::copy(&mut source_file, &mut dest_file).map_err(|e| { std::io::copy(&mut source_file, &mut dest_file)
Pkg6RepoError::IoError(e) .map_err(|e| Pkg6RepoError::IoError(e))?;
})?;
} }
// Add the file to the transaction // Add the file to the transaction
@ -426,7 +471,7 @@ fn url_decode(s: &str) -> String {
while i < s.len() { while i < s.len() {
if s[i..].starts_with("%") && i + 2 < s.len() { if s[i..].starts_with("%") && i + 2 < s.len() {
if let Ok(hex) = u8::from_str_radix(&s[i+1..i+3], 16) { if let Ok(hex) = u8::from_str_radix(&s[i + 1..i + 3], 16) {
result.push(hex as char); result.push(hex as char);
i += 3; i += 3;
} else { } else {

View file

@ -1,8 +1,8 @@
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use libips::repository::{ use libips::repository::{
FileBackend, ReadableRepository, RepositoryVersion, FileBackend, REPOSITORY_CONFIG_FILENAME, ReadableRepository, RepositoryVersion,
WritableRepository, REPOSITORY_CONFIG_FILENAME, WritableRepository,
}; };
use std::fs; use std::fs;
use std::path::PathBuf; use std::path::PathBuf;

View file

@ -96,7 +96,8 @@ fn setup_test_env() -> Result<()> {
// Skip the bin directory // Skip the bin directory
if path.is_dir() && path.file_name().unwrap_or_default() != "bin" { if path.is_dir() && path.file_name().unwrap_or_default() != "bin" {
fs::remove_dir_all(&path).context(format!("Failed to remove directory: {:?}", path))?; fs::remove_dir_all(&path)
.context(format!("Failed to remove directory: {:?}", path))?;
} else if path.is_file() { } else if path.is_file() {
fs::remove_file(&path).context(format!("Failed to remove file: {:?}", path))?; fs::remove_file(&path).context(format!("Failed to remove file: {:?}", path))?;
} }
@ -122,14 +123,20 @@ fn setup_test_env() -> Result<()> {
println!("Creating prototype directory structure..."); println!("Creating prototype directory structure...");
// Create some directories // Create some directories
fs::create_dir_all(format!("{}/usr/bin", PROTOTYPE_DIR)).context("Failed to create usr/bin directory")?; fs::create_dir_all(format!("{}/usr/bin", PROTOTYPE_DIR))
fs::create_dir_all(format!("{}/usr/share/doc/example", PROTOTYPE_DIR)).context("Failed to create usr/share/doc/example directory")?; .context("Failed to create usr/bin directory")?;
fs::create_dir_all(format!("{}/etc/config", PROTOTYPE_DIR)).context("Failed to create etc/config directory")?; fs::create_dir_all(format!("{}/usr/share/doc/example", PROTOTYPE_DIR))
.context("Failed to create usr/share/doc/example directory")?;
fs::create_dir_all(format!("{}/etc/config", PROTOTYPE_DIR))
.context("Failed to create etc/config directory")?;
// Create some files // Create some files
let hello_script = "#!/bin/sh\necho 'Hello, World!'"; let hello_script = "#!/bin/sh\necho 'Hello, World!'";
let mut hello_file = File::create(format!("{}/usr/bin/hello", PROTOTYPE_DIR)).context("Failed to create hello script")?; let mut hello_file = File::create(format!("{}/usr/bin/hello", PROTOTYPE_DIR))
hello_file.write_all(hello_script.as_bytes()).context("Failed to write hello script")?; .context("Failed to create hello script")?;
hello_file
.write_all(hello_script.as_bytes())
.context("Failed to write hello script")?;
// Make the script executable // Make the script executable
#[cfg(unix)] #[cfg(unix)]
@ -144,14 +151,21 @@ fn setup_test_env() -> Result<()> {
} }
let readme_content = "This is an example document."; let readme_content = "This is an example document.";
let mut readme_file = File::create(format!("{}/usr/share/doc/example/README.txt", PROTOTYPE_DIR)) let mut readme_file = File::create(format!(
"{}/usr/share/doc/example/README.txt",
PROTOTYPE_DIR
))
.context("Failed to create README.txt")?; .context("Failed to create README.txt")?;
readme_file.write_all(readme_content.as_bytes()).context("Failed to write README.txt")?; readme_file
.write_all(readme_content.as_bytes())
.context("Failed to write README.txt")?;
let config_content = "# Example configuration file\nvalue=42"; let config_content = "# Example configuration file\nvalue=42";
let mut config_file = File::create(format!("{}/etc/config/example.conf", PROTOTYPE_DIR)) let mut config_file = File::create(format!("{}/etc/config/example.conf", PROTOTYPE_DIR))
.context("Failed to create example.conf")?; .context("Failed to create example.conf")?;
config_file.write_all(config_content.as_bytes()).context("Failed to write example.conf")?; config_file
.write_all(config_content.as_bytes())
.context("Failed to write example.conf")?;
// Create a simple manifest // Create a simple manifest
println!("Creating package manifest..."); println!("Creating package manifest...");
@ -170,7 +184,9 @@ dir path=etc/config mode=0755 owner=root group=sys
let mut example_file = File::create(format!("{}/example.p5m", MANIFEST_DIR)) let mut example_file = File::create(format!("{}/example.p5m", MANIFEST_DIR))
.context("Failed to create example.p5m")?; .context("Failed to create example.p5m")?;
example_file.write_all(example_manifest.as_bytes()).context("Failed to write example.p5m")?; example_file
.write_all(example_manifest.as_bytes())
.context("Failed to write example.p5m")?;
// Create a second manifest for testing multiple packages // Create a second manifest for testing multiple packages
let example2_manifest = r#"set name=pkg.fmri value=pkg://test/example2@1.0.0 let example2_manifest = r#"set name=pkg.fmri value=pkg://test/example2@1.0.0
@ -186,7 +202,9 @@ dir path=usr/share/doc/example mode=0755 owner=root group=bin
let mut example2_file = File::create(format!("{}/example2.p5m", MANIFEST_DIR)) let mut example2_file = File::create(format!("{}/example2.p5m", MANIFEST_DIR))
.context("Failed to create example2.p5m")?; .context("Failed to create example2.p5m")?;
example2_file.write_all(example2_manifest.as_bytes()).context("Failed to write example2.p5m")?; example2_file
.write_all(example2_manifest.as_bytes())
.context("Failed to write example2.p5m")?;
println!("Test environment setup complete!"); println!("Test environment setup complete!");
println!("Prototype directory: {}", PROTOTYPE_DIR); println!("Prototype directory: {}", PROTOTYPE_DIR);
@ -244,7 +262,14 @@ fn fmt() -> Result<()> {
/// Run clippy /// Run clippy
fn clippy() -> Result<()> { fn clippy() -> Result<()> {
Command::new("cargo") Command::new("cargo")
.args(["clippy", "--all-targets", "--all-features", "--", "-D", "warnings"]) .args([
"clippy",
"--all-targets",
"--all-features",
"--",
"-D",
"warnings",
])
.status() .status()
.context("Failed to run clippy")?; .context("Failed to run clippy")?;