# APT Wrapper A simple DNF-like API wrapper around APT for porting rpm-ostree to apt-ostree. ## Purpose This library provides a simple transaction interface that mimics DNF's imperative model, making it easier to adapt rpm-ostree code for Debian/Ubuntu systems. ## Features - **Simple transaction interface**: `add_package()`, `resolve()`, `commit()`, `rollback()` - **DNF-like API**: Easy to port from rpm-ostree - **Version-based rollback**: Track versions and restore previous states - **Minimal dependencies**: Only `anyhow` - **~500 lines total**: Focused and maintainable ## Quick Start ```rust use apt_wrapper::{AptTransaction, init}; fn main() -> Result<(), Box> { // Initialize init()?; // Create transaction let mut tx = AptTransaction::new()?; // Add packages tx.add_package("vim")?; tx.add_package("git")?; // Resolve dependencies tx.resolve()?; // Commit transaction tx.commit()?; // If something goes wrong, rollback // tx.rollback()?; Ok(()) } ``` ## API ### AptTransaction ```rust pub struct AptTransaction { packages: Vec, newly_installed: HashSet, // packages that weren't installed before upgraded: HashMap, // package -> old_version for upgrades config: AptConfig, // APT configuration options } impl AptTransaction { pub fn new() -> Result; // Create new transaction (default config) pub fn with_config(config: AptConfig) -> Result; // Create with custom config pub fn for_testing() -> Result; // Create for testing (dry run) pub fn add_package(&mut self, name: &str) -> Result<()>; // Add package pub fn resolve(&self) -> Result<()>; // Resolve dependencies pub fn commit(&mut self) -> Result<()>; // Commit transaction pub fn rollback(&self) -> Result<()>; // Rollback transaction pub fn packages(&self) -> &[String]; // Get package list pub fn changed_packages(&self) -> Vec; // Get changed packages pub fn is_empty(&self) -> bool; // Check if empty pub fn config(&self) -> &AptConfig; // Get configuration pub fn set_config(&mut self, config: AptConfig); // Update configuration pub fn enable_dry_run(&mut self); // Enable dry run mode pub fn disable_dry_run(&mut self); // Disable dry run mode pub fn enable_quiet(&mut self); // Enable quiet mode pub fn disable_quiet(&mut self); // Disable quiet mode } ``` ### Utility Functions ```rust pub fn init() -> Result<()>; // Initialize pub fn search_packages(query: &str) -> Result>; // Search packages pub fn is_package_installed(name: &str) -> Result; // Check installed pub fn get_package_info(name: &str) -> Result; // Get package info ``` ### Configuration ```rust pub struct AptConfig { pub no_install_recommends: bool, // Don't install recommended packages pub no_install_suggests: bool, // Don't install suggested packages pub assume_yes: bool, // Assume yes to all prompts pub quiet: bool, // Quiet output pub dry_run: bool, // Dry run mode (don't actually execute) } impl AptConfig { pub fn new() -> Self; // Create with default values pub fn for_testing() -> Self; // Create for testing (dry run + quiet) } ``` ## Installation Add to your `Cargo.toml`: ```toml [dependencies] apt-wrapper = "0.1.0" ``` ## Using as a Crate ### Adding to Your Project 1. **Add the dependency** to your `Cargo.toml`: ```toml [dependencies] apt-wrapper = "0.1.0" ``` 2. **Import the crate** in your Rust code: ```rust use apt_wrapper::{AptTransaction, AptConfig, init, search_packages, is_package_installed}; ``` 3. **Initialize the library** before use: ```rust fn main() -> Result<(), Box> { // Always call init() first init()?; // Now you can use the library let mut tx = AptTransaction::new()?; // ... rest of your code } ``` ### Crate Features The crate provides these main components: - **`AptTransaction`**: Main transaction interface for package operations - **`AptConfig`**: Configuration options for APT behavior - **`init()`**: Initialize the library (required before use) - **`search_packages()`**: Search for available packages - **`is_package_installed()`**: Check if a package is installed - **`get_package_info()`**: Get detailed package information ### Error Handling The crate uses `anyhow::Result` for error handling. All functions return `Result`: ```rust use anyhow::Result; fn example() -> Result<()> { let mut tx = AptTransaction::new()?; // Returns Result tx.add_package("vim")?; // Returns Result<(), anyhow::Error> tx.commit()?; // Returns Result<(), anyhow::Error> Ok(()) } ``` ### Complete Example Here's a complete example of using the crate: ```rust use apt_wrapper::{AptTransaction, AptConfig, init, search_packages, is_package_installed}; use anyhow::Result; fn main() -> Result<()> { // Initialize the library init()?; // Search for packages let editors = search_packages("editor")?; println!("Available editors: {:?}", editors); // Check if vim is installed if !is_package_installed("vim")? { println!("vim is not installed, installing..."); // Create transaction let mut tx = AptTransaction::new()?; tx.add_package("vim")?; tx.add_package("git")?; // Resolve and commit tx.resolve()?; tx.commit()?; println!("Packages installed successfully!"); } else { println!("vim is already installed"); } Ok(()) } ``` ### Building Your Project Once you've added the dependency, build your project normally: ```bash cargo build cargo run ``` The crate will automatically handle APT integration and provide the DNF-like interface for your application. ## Usage ### Basic Transaction ```rust use apt_wrapper::AptTransaction; let mut tx = AptTransaction::new()?; tx.add_package("vim")?; tx.add_package("git")?; tx.resolve()?; tx.commit()?; ``` ### Search Packages ```rust use apt_wrapper::search_packages; let packages = search_packages("editor")?; for package in packages { println!("Found: {}", package); } ``` ### Check Installation ```rust use apt_wrapper::is_package_installed; if is_package_installed("vim")? { println!("vim is installed"); } ``` ### Rollback Support ```rust use apt_wrapper::AptTransaction; let mut tx = AptTransaction::new()?; tx.add_package("vim")?; tx.add_package("git")?; tx.resolve()?; // Commit the transaction tx.commit()?; // If something goes wrong later, rollback tx.rollback()?; // Check what was changed println!("Changed packages: {:?}", tx.changed_packages()); ``` ### Configuration Options ```rust use apt_wrapper::{AptTransaction, AptConfig}; // Create with custom configuration let config = AptConfig { no_install_recommends: true, no_install_suggests: true, assume_yes: true, quiet: false, dry_run: true, }; let mut tx = AptTransaction::with_config(config)?; // Or modify configuration at runtime tx.enable_dry_run(); tx.enable_quiet(); // For testing let mut test_tx = AptTransaction::for_testing()?; ``` ## Testing ```bash cargo test ``` ## Examples ```bash cargo run --example simple_usage cargo run --example configuration_usage ``` ## Design Philosophy This wrapper is designed to be: 1. **Simple**: Minimal API surface, easy to understand 2. **Focused**: Only what's needed for apt-ostree porting 3. **DNF-like**: Familiar interface for rpm-ostree developers 4. **Minimal**: ~500 lines total, no complex abstractions ## Differences from DNF - **APT is declarative**: Dependencies are resolved automatically - **No complex repo management**: APT uses simple text files - **Simpler error handling**: APT provides clear error messages - **Transaction rollback**: Implemented two-phase rollback (remove new, downgrade upgraded) ## OSTree Integration For atomic operations, use OSTree's native checkpoint/rollback: ```rust // 1. Create OSTree checkpoint let checkpoint = ostree_create_checkpoint()?; // 2. Run APT transaction let mut tx = AptTransaction::new()?; tx.add_package("vim")?; tx.commit()?; // 3. Commit or rollback based on result if success { ostree_commit_changes()?; } else { ostree_rollback_to_checkpoint(checkpoint)?; } ``` ## License MIT License - see [LICENSE](LICENSE) file for details. ## Documentation - [FFI Bridge Documentation](docs/ffi-bridge.md) - C++ integration guide - [Rollback Implementation](docs/rollbacks.md) - How rollback functionality works ## Contributing This is a focused tool for apt-ostree. Contributions should maintain simplicity and focus on the core use case.