Initial commit: APT-DNF Bridge workspace
- Core crate: Minimal shell-out implementation - Advanced crate: Pluggable backends and enhanced features - Main crate: Re-exports core + optional advanced features - Feature flags: Users choose complexity level - Examples: Working demonstrations of both approaches - Documentation: Clear README explaining the structure Implements the refined two-crate approach with workspace + feature flags.
This commit is contained in:
commit
06cafa0366
24 changed files with 2790 additions and 0 deletions
63
examples/atomicity_notes.rs
Normal file
63
examples/atomicity_notes.rs
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
//! Example demonstrating the atomicity limitations of the APT-DNF Bridge.
|
||||
//!
|
||||
//! This example shows how the APT-DNF Bridge provides transaction-like semantics
|
||||
//! but does NOT provide true system-level atomicity.
|
||||
|
||||
use apt_dnf_bridge::{Package, Transaction};
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
println!("APT-DNF Bridge - Atomicity Notes Example");
|
||||
println!("=====================================");
|
||||
|
||||
// Create a transaction
|
||||
let mut tx = Transaction::new();
|
||||
|
||||
// Add some operations
|
||||
let vim = Package::new("vim", "2:8.1.2269-1ubuntu5.14", "amd64");
|
||||
let nano = Package::new("nano", "2.9.3-2", "amd64");
|
||||
|
||||
tx.add_install(vim).await?;
|
||||
tx.add_install(nano).await?;
|
||||
|
||||
println!("Created transaction with {} operations", tx.len());
|
||||
|
||||
// Resolve dependencies (this will validate the transaction)
|
||||
println!("Resolving dependencies...");
|
||||
tx.resolve().await?;
|
||||
println!("✓ Dependencies resolved successfully");
|
||||
|
||||
// IMPORTANT: The commit() method does NOT provide true atomicity
|
||||
println!("\n⚠️ IMPORTANT ATOMICITY NOTES:");
|
||||
println!(" • This transaction provides transaction-like semantics");
|
||||
println!(" • It does NOT provide true system-level atomicity");
|
||||
println!(" • If the operation fails midway, the system could be left in a corrupted state");
|
||||
println!(" • For true atomicity, apt-ostree must handle this at a higher level");
|
||||
println!(" • This might involve operating on a temporary directory and performing an atomic pivot_root");
|
||||
|
||||
println!("\nTransaction operations:");
|
||||
for (i, op) in tx.operations().iter().enumerate() {
|
||||
match op {
|
||||
apt_dnf_bridge::Operation::Install(pkg) => {
|
||||
println!(" {}: Install {}", i + 1, pkg.name);
|
||||
}
|
||||
apt_dnf_bridge::Operation::Remove(pkg) => {
|
||||
println!(" {}: Remove {}", i + 1, pkg.name);
|
||||
}
|
||||
apt_dnf_bridge::Operation::Upgrade(pkg) => {
|
||||
println!(" {}: Upgrade {}", i + 1, pkg.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
println!("\nTo commit this transaction (commented out for safety):");
|
||||
println!(" tx.commit().await?;");
|
||||
|
||||
println!("\nFor apt-ostree integration, consider:");
|
||||
println!(" 1. Operating on a temporary directory");
|
||||
println!(" 2. Performing all APT operations there");
|
||||
println!(" 3. Using atomic pivot_root to switch to the new system state");
|
||||
println!(" 4. Rolling back if any step fails");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
105
examples/backend_selection.rs
Normal file
105
examples/backend_selection.rs
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
//! Example demonstrating backend selection and switching.
|
||||
|
||||
use apt_dnf_bridge::{
|
||||
BackendConfig, Package, TransactionV2, PackageDatabaseV2,
|
||||
};
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
println!("APT-DNF Bridge - Backend Selection Example");
|
||||
println!("======================================");
|
||||
|
||||
// 1. Auto-detect the best available backend
|
||||
println!("1. Auto-detecting best backend...");
|
||||
let tx = TransactionV2::new().await?;
|
||||
let (backend_name, backend_version) = tx.backend_info();
|
||||
println!(" Selected backend: {} v{}", backend_name, backend_version);
|
||||
|
||||
// 2. Create a transaction with shell backend explicitly
|
||||
println!("\n2. Creating transaction with shell backend...");
|
||||
let config = BackendConfig {
|
||||
enable_logging: true,
|
||||
use_cache: true,
|
||||
max_cache_size: 1000,
|
||||
command_timeout: Some(300),
|
||||
};
|
||||
let shell_tx = TransactionV2::with_shell_backend(config).await?;
|
||||
let (shell_name, shell_version) = shell_tx.backend_info();
|
||||
println!(" Shell backend: {} v{}", shell_name, shell_version);
|
||||
|
||||
// 3. Create a package database with mock backend for testing
|
||||
println!("\n3. Creating package database with mock backend...");
|
||||
let mock_config = BackendConfig {
|
||||
enable_logging: true,
|
||||
use_cache: true,
|
||||
max_cache_size: 100,
|
||||
command_timeout: Some(30),
|
||||
};
|
||||
let mut mock_db = PackageDatabaseV2::with_mock_backend(mock_config).await?;
|
||||
let (_mock_name, mock_version) = mock_db.backend_info();
|
||||
println!(" Mock backend: v{}", mock_version);
|
||||
|
||||
// 4. Demonstrate backend capabilities
|
||||
println!("\n4. Testing backend capabilities...");
|
||||
|
||||
// Test mock backend
|
||||
println!(" Testing mock backend:");
|
||||
let packages = mock_db.find_packages("vim").await?;
|
||||
println!(" Found {} packages matching 'vim'", packages.len());
|
||||
|
||||
if let Some(pkg_info) = mock_db.get_package_info("vim").await? {
|
||||
println!(" Package info: {} {}", pkg_info.name, pkg_info.version);
|
||||
}
|
||||
|
||||
// Test shell backend (if available)
|
||||
println!(" Testing shell backend:");
|
||||
let mut shell_db = PackageDatabaseV2::with_shell_backend(BackendConfig::default()).await?;
|
||||
let shell_packages = shell_db.find_packages("vim").await?;
|
||||
println!(" Found {} packages matching 'vim'", shell_packages.len());
|
||||
|
||||
// 5. Demonstrate transaction with different backends
|
||||
println!("\n5. Testing transactions with different backends...");
|
||||
|
||||
let vim = Package::new("vim", "2:8.1.2269-1ubuntu5.14", "amd64");
|
||||
|
||||
// Mock transaction
|
||||
let mut mock_tx = TransactionV2::with_mock_backend(BackendConfig::default()).await?;
|
||||
mock_tx.add_install(vim.clone()).await?;
|
||||
println!(" Mock transaction: {} operations", mock_tx.len());
|
||||
|
||||
let resolution = mock_tx.resolve().await?;
|
||||
println!(" Mock resolution: resolvable={}, summary='{}'",
|
||||
resolution.resolvable, resolution.summary);
|
||||
|
||||
// Shell transaction (if available)
|
||||
let mut shell_tx = TransactionV2::with_shell_backend(BackendConfig::default()).await?;
|
||||
shell_tx.add_install(vim).await?;
|
||||
println!(" Shell transaction: {} operations", shell_tx.len());
|
||||
|
||||
let shell_resolution = shell_tx.resolve().await?;
|
||||
println!(" Shell resolution: resolvable={}, summary='{}'",
|
||||
shell_resolution.resolvable, shell_resolution.summary);
|
||||
|
||||
// 6. Show backend statistics
|
||||
println!("\n6. Backend statistics:");
|
||||
let mock_stats = mock_db.get_backend_stats().await?;
|
||||
println!(" Mock backend stats:");
|
||||
println!(" Commands executed: {}", mock_stats.commands_executed);
|
||||
println!(" Packages queried: {}", mock_stats.packages_queried);
|
||||
println!(" Cache hit rate: {:.2}%", mock_stats.cache_hit_rate * 100.0);
|
||||
|
||||
let shell_stats = shell_db.get_backend_stats().await?;
|
||||
println!(" Shell backend stats:");
|
||||
println!(" Commands executed: {}", shell_stats.commands_executed);
|
||||
println!(" Packages queried: {}", shell_stats.packages_queried);
|
||||
println!(" Cache hit rate: {:.2}%", shell_stats.cache_hit_rate * 100.0);
|
||||
|
||||
println!("\n✅ Backend selection example completed successfully!");
|
||||
println!("\n💡 Key benefits of pluggable backends:");
|
||||
println!(" • Start with simple shell backend (reliable)");
|
||||
println!(" • Switch to libapt backend when available (more powerful)");
|
||||
println!(" • Use mock backend for testing (fast, predictable)");
|
||||
println!(" • Same API works with all backends");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
54
examples/basic_usage.rs
Normal file
54
examples/basic_usage.rs
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
//! Basic usage example for the APT-DNF Bridge crate.
|
||||
|
||||
use apt_dnf_bridge::{Package, Repository, Transaction};
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
println!("APT-DNF Bridge - Basic Usage Example");
|
||||
println!("=================================");
|
||||
|
||||
// Create a package
|
||||
let vim = Package::new("vim", "2:8.1.2269-1ubuntu5.14", "amd64");
|
||||
println!("Created package: {}", vim.spec());
|
||||
|
||||
// Create a transaction with logging enabled
|
||||
let mut tx = Transaction::new_with_logging();
|
||||
println!("Created empty transaction with logging enabled");
|
||||
|
||||
// Add operations to the transaction
|
||||
tx.add_install(vim.clone()).await?;
|
||||
println!("Added install operation for: {}", vim.name);
|
||||
|
||||
// Check transaction status
|
||||
println!("Transaction has {} operations", tx.len());
|
||||
println!("Transaction is empty: {}", tx.is_empty());
|
||||
|
||||
// Note: In a real scenario, you would call:
|
||||
// tx.resolve().await?; // Resolve dependencies
|
||||
// tx.commit().await?; // Commit the transaction
|
||||
|
||||
// But for this example, we'll just show the structure
|
||||
println!("Transaction operations:");
|
||||
for (i, op) in tx.operations().iter().enumerate() {
|
||||
match op {
|
||||
apt_dnf_bridge::Operation::Install(pkg) => {
|
||||
println!(" {}: Install {}", i + 1, pkg.name);
|
||||
}
|
||||
apt_dnf_bridge::Operation::Remove(pkg) => {
|
||||
println!(" {}: Remove {}", i + 1, pkg.name);
|
||||
}
|
||||
apt_dnf_bridge::Operation::Upgrade(pkg) => {
|
||||
println!(" {}: Upgrade {}", i + 1, pkg.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create a repository
|
||||
let mut repo = Repository::new("debian", "http://deb.debian.org/debian")?;
|
||||
repo.add_component("main");
|
||||
repo.add_component("contrib");
|
||||
println!("Created repository: {} at {}", repo.name, repo.url);
|
||||
println!("Components: {:?}", repo.components);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
60
examples/package_query.rs
Normal file
60
examples/package_query.rs
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
//! Package querying example for the APT-DNF Bridge crate.
|
||||
|
||||
use apt_dnf_bridge::PackageDatabase;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
println!("APT-DNF Bridge - Package Query Example");
|
||||
println!("===================================");
|
||||
|
||||
let mut db = PackageDatabase::new_with_logging();
|
||||
|
||||
// Search for packages
|
||||
println!("Searching for packages matching 'vim'...");
|
||||
let packages = db.find_packages("vim").await?;
|
||||
println!("Found {} packages:", packages.len());
|
||||
|
||||
for (i, pkg) in packages.iter().take(5).enumerate() {
|
||||
println!(" {}: {}", i + 1, pkg.name);
|
||||
}
|
||||
|
||||
if packages.len() > 5 {
|
||||
println!(" ... and {} more", packages.len() - 5);
|
||||
}
|
||||
|
||||
// Get detailed information about a specific package
|
||||
println!("\nGetting detailed info for 'vim'...");
|
||||
if let Some(pkg_info) = db.get_package_info("vim").await? {
|
||||
println!("Package: {}", pkg_info.name);
|
||||
println!("Version: {}", pkg_info.version);
|
||||
println!("Architecture: {}", pkg_info.architecture);
|
||||
if let Some(desc) = &pkg_info.description {
|
||||
println!("Description: {}", desc);
|
||||
}
|
||||
if let Some(size) = pkg_info.size {
|
||||
println!("Size: {} bytes", size);
|
||||
}
|
||||
} else {
|
||||
println!("Package 'vim' not found");
|
||||
}
|
||||
|
||||
// Check if a package is installed
|
||||
println!("\nChecking if 'vim' is installed...");
|
||||
let is_installed = db.is_installed("vim").await?;
|
||||
println!("vim is installed: {}", is_installed);
|
||||
|
||||
// List some installed packages
|
||||
println!("\nListing some installed packages...");
|
||||
let installed = db.get_installed_packages().await?;
|
||||
println!("Found {} installed packages", installed.len());
|
||||
|
||||
for (i, pkg) in installed.iter().take(10).enumerate() {
|
||||
println!(" {}: {} {}", i + 1, pkg.name, pkg.version);
|
||||
}
|
||||
|
||||
if installed.len() > 10 {
|
||||
println!(" ... and {} more", installed.len() - 10);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue