299 lines
6.7 KiB
Markdown
299 lines
6.7 KiB
Markdown
# 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` and `thiserror`
|
|
- **~250 lines total**: Focused and maintainable
|
|
|
|
## Quick Start
|
|
|
|
```rust
|
|
use apt_wrapper::{AptTransaction, init};
|
|
|
|
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|
// 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<String>,
|
|
}
|
|
|
|
impl AptTransaction {
|
|
pub fn new() -> Result<Self>; // Create new transaction
|
|
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<String>; // Get changed packages
|
|
pub fn is_empty(&self) -> bool; // Check if empty
|
|
}
|
|
```
|
|
|
|
### Utility Functions
|
|
|
|
```rust
|
|
pub fn init() -> Result<()>; // Initialize
|
|
pub fn search_packages(query: &str) -> Result<Vec<String>>; // Search packages
|
|
pub fn is_package_installed(name: &str) -> Result<bool>; // Check installed
|
|
pub fn get_package_info(name: &str) -> Result<AptPackage>; // Get package info
|
|
```
|
|
|
|
## 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, init, search_packages, is_package_installed};
|
|
```
|
|
|
|
3. **Initialize the library** before use:
|
|
|
|
```rust
|
|
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|
// 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
|
|
- **`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<T, anyhow::Error>`:
|
|
|
|
```rust
|
|
use anyhow::Result;
|
|
|
|
fn example() -> Result<()> {
|
|
let mut tx = AptTransaction::new()?; // Returns Result<AptTransaction, anyhow::Error>
|
|
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, 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());
|
|
```
|
|
|
|
## Testing
|
|
|
|
```bash
|
|
cargo test
|
|
```
|
|
|
|
## Examples
|
|
|
|
```bash
|
|
cargo run --example simple_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**: ~200 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
|
|
- **No transaction rollback**: APT doesn't have built-in rollback
|
|
|
|
## 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.
|
|
|
|
## Contributing
|
|
|
|
This is a focused tool for apt-ostree. Contributions should maintain simplicity and focus on the core use case.
|