mirror of
https://codeberg.org/Toasterson/ips.git
synced 2026-04-10 13:20:42 +00:00
Add debugging utilities and improved package listing
- Add support for debugging database tables with `--stats`, `--dump-all`, and `--dump-table` options. - Introduce CLI command `DebugDb` for inspecting catalog, obsoleted, and installed tables. - Enhance `List` command to include an `--all` flag for listing all available packages. - Update logging for database operations and package queries. - Add methods to dump and analyze table contents in `InstalledPackages` and `ImageCatalog`.
This commit is contained in:
parent
0ec1c1928a
commit
92cce0f767
3 changed files with 483 additions and 3 deletions
|
|
@ -90,6 +90,206 @@ impl ImageCatalog {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Dump the contents of a specific table to stdout for debugging
|
||||||
|
pub fn dump_table(&self, table_name: &str) -> Result<()> {
|
||||||
|
// Open the database
|
||||||
|
let db = Database::open(&self.db_path)
|
||||||
|
.map_err(|e| CatalogError::Database(format!("Failed to open database: {}", e)))?;
|
||||||
|
|
||||||
|
// Begin a read transaction
|
||||||
|
let tx = db.begin_read()
|
||||||
|
.map_err(|e| CatalogError::Database(format!("Failed to begin transaction: {}", e)))?;
|
||||||
|
|
||||||
|
// Determine which table to dump
|
||||||
|
match table_name {
|
||||||
|
"catalog" => self.dump_catalog_table(&tx)?,
|
||||||
|
"obsoleted" => self.dump_obsoleted_table(&tx)?,
|
||||||
|
"installed" => self.dump_installed_table(&tx)?,
|
||||||
|
_ => return Err(CatalogError::Database(format!("Unknown table: {}", table_name))),
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Dump the contents of all tables to stdout for debugging
|
||||||
|
pub fn dump_all_tables(&self) -> Result<()> {
|
||||||
|
// Open the database
|
||||||
|
let db = Database::open(&self.db_path)
|
||||||
|
.map_err(|e| CatalogError::Database(format!("Failed to open database: {}", e)))?;
|
||||||
|
|
||||||
|
// Begin a read transaction
|
||||||
|
let tx = db.begin_read()
|
||||||
|
.map_err(|e| CatalogError::Database(format!("Failed to begin transaction: {}", e)))?;
|
||||||
|
|
||||||
|
println!("=== CATALOG TABLE ===");
|
||||||
|
let _ = self.dump_catalog_table(&tx);
|
||||||
|
|
||||||
|
println!("\n=== OBSOLETED TABLE ===");
|
||||||
|
let _ = self.dump_obsoleted_table(&tx);
|
||||||
|
|
||||||
|
println!("\n=== INSTALLED TABLE ===");
|
||||||
|
let _ = self.dump_installed_table(&tx);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Dump the contents of the catalog table
|
||||||
|
fn dump_catalog_table(&self, tx: &redb::ReadTransaction) -> Result<()> {
|
||||||
|
match tx.open_table(CATALOG_TABLE) {
|
||||||
|
Ok(table) => {
|
||||||
|
let mut count = 0;
|
||||||
|
for entry_result in table.iter().map_err(|e| CatalogError::Database(format!("Failed to iterate catalog table: {}", e)))? {
|
||||||
|
let (key, value) = entry_result.map_err(|e| CatalogError::Database(format!("Failed to get entry from catalog table: {}", e)))?;
|
||||||
|
let key_str = key.value();
|
||||||
|
|
||||||
|
// Try to deserialize the manifest
|
||||||
|
match serde_json::from_slice::<Manifest>(value.value()) {
|
||||||
|
Ok(manifest) => {
|
||||||
|
// Extract the publisher from the FMRI attribute
|
||||||
|
let publisher = manifest.attributes.iter()
|
||||||
|
.find(|attr| attr.key == "pkg.fmri")
|
||||||
|
.and_then(|attr| attr.values.get(0).cloned())
|
||||||
|
.unwrap_or_else(|| "unknown".to_string());
|
||||||
|
|
||||||
|
println!("Key: {}", key_str);
|
||||||
|
println!(" FMRI: {}", publisher);
|
||||||
|
println!(" Attributes: {}", manifest.attributes.len());
|
||||||
|
println!(" Files: {}", manifest.files.len());
|
||||||
|
println!(" Directories: {}", manifest.directories.len());
|
||||||
|
println!(" Dependencies: {}", manifest.dependencies.len());
|
||||||
|
},
|
||||||
|
Err(e) => {
|
||||||
|
println!("Key: {}", key_str);
|
||||||
|
println!(" Error deserializing manifest: {}", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
count += 1;
|
||||||
|
}
|
||||||
|
println!("Total entries in catalog table: {}", count);
|
||||||
|
Ok(())
|
||||||
|
},
|
||||||
|
Err(e) => {
|
||||||
|
println!("Error opening catalog table: {}", e);
|
||||||
|
Err(CatalogError::Database(format!("Failed to open catalog table: {}", e)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Dump the contents of the obsoleted table
|
||||||
|
fn dump_obsoleted_table(&self, tx: &redb::ReadTransaction) -> Result<()> {
|
||||||
|
match tx.open_table(OBSOLETED_TABLE) {
|
||||||
|
Ok(table) => {
|
||||||
|
let mut count = 0;
|
||||||
|
for entry_result in table.iter().map_err(|e| CatalogError::Database(format!("Failed to iterate obsoleted table: {}", e)))? {
|
||||||
|
let (key, _) = entry_result.map_err(|e| CatalogError::Database(format!("Failed to get entry from obsoleted table: {}", e)))?;
|
||||||
|
let key_str = key.value();
|
||||||
|
|
||||||
|
println!("Key: {}", key_str);
|
||||||
|
count += 1;
|
||||||
|
}
|
||||||
|
println!("Total entries in obsoleted table: {}", count);
|
||||||
|
Ok(())
|
||||||
|
},
|
||||||
|
Err(e) => {
|
||||||
|
println!("Error opening obsoleted table: {}", e);
|
||||||
|
Err(CatalogError::Database(format!("Failed to open obsoleted table: {}", e)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Dump the contents of the installed table
|
||||||
|
fn dump_installed_table(&self, tx: &redb::ReadTransaction) -> Result<()> {
|
||||||
|
match tx.open_table(INSTALLED_TABLE) {
|
||||||
|
Ok(table) => {
|
||||||
|
let mut count = 0;
|
||||||
|
for entry_result in table.iter().map_err(|e| CatalogError::Database(format!("Failed to iterate installed table: {}", e)))? {
|
||||||
|
let (key, value) = entry_result.map_err(|e| CatalogError::Database(format!("Failed to get entry from installed table: {}", e)))?;
|
||||||
|
let key_str = key.value();
|
||||||
|
|
||||||
|
// Try to deserialize the manifest
|
||||||
|
match serde_json::from_slice::<Manifest>(value.value()) {
|
||||||
|
Ok(manifest) => {
|
||||||
|
// Extract the publisher from the FMRI attribute
|
||||||
|
let publisher = manifest.attributes.iter()
|
||||||
|
.find(|attr| attr.key == "pkg.fmri")
|
||||||
|
.and_then(|attr| attr.values.get(0).cloned())
|
||||||
|
.unwrap_or_else(|| "unknown".to_string());
|
||||||
|
|
||||||
|
println!("Key: {}", key_str);
|
||||||
|
println!(" FMRI: {}", publisher);
|
||||||
|
println!(" Attributes: {}", manifest.attributes.len());
|
||||||
|
println!(" Files: {}", manifest.files.len());
|
||||||
|
println!(" Directories: {}", manifest.directories.len());
|
||||||
|
println!(" Dependencies: {}", manifest.dependencies.len());
|
||||||
|
},
|
||||||
|
Err(e) => {
|
||||||
|
println!("Key: {}", key_str);
|
||||||
|
println!(" Error deserializing manifest: {}", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
count += 1;
|
||||||
|
}
|
||||||
|
println!("Total entries in installed table: {}", count);
|
||||||
|
Ok(())
|
||||||
|
},
|
||||||
|
Err(e) => {
|
||||||
|
println!("Error opening installed table: {}", e);
|
||||||
|
Err(CatalogError::Database(format!("Failed to open installed table: {}", e)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get database statistics
|
||||||
|
pub fn get_db_stats(&self) -> Result<()> {
|
||||||
|
// Open the database
|
||||||
|
let db = Database::open(&self.db_path)
|
||||||
|
.map_err(|e| CatalogError::Database(format!("Failed to open database: {}", e)))?;
|
||||||
|
|
||||||
|
// Begin a read transaction
|
||||||
|
let tx = db.begin_read()
|
||||||
|
.map_err(|e| CatalogError::Database(format!("Failed to begin transaction: {}", e)))?;
|
||||||
|
|
||||||
|
// Get table statistics
|
||||||
|
let mut catalog_count = 0;
|
||||||
|
let mut obsoleted_count = 0;
|
||||||
|
let mut installed_count = 0;
|
||||||
|
|
||||||
|
// Count catalog entries
|
||||||
|
if let Ok(table) = tx.open_table(CATALOG_TABLE) {
|
||||||
|
for result in table.iter().map_err(|e| CatalogError::Database(format!("Failed to iterate catalog table: {}", e)))? {
|
||||||
|
let _ = result.map_err(|e| CatalogError::Database(format!("Failed to get entry from catalog table: {}", e)))?;
|
||||||
|
catalog_count += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Count obsoleted entries
|
||||||
|
if let Ok(table) = tx.open_table(OBSOLETED_TABLE) {
|
||||||
|
for result in table.iter().map_err(|e| CatalogError::Database(format!("Failed to iterate obsoleted table: {}", e)))? {
|
||||||
|
let _ = result.map_err(|e| CatalogError::Database(format!("Failed to get entry from obsoleted table: {}", e)))?;
|
||||||
|
obsoleted_count += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Count installed entries
|
||||||
|
if let Ok(table) = tx.open_table(INSTALLED_TABLE) {
|
||||||
|
for result in table.iter().map_err(|e| CatalogError::Database(format!("Failed to iterate installed table: {}", e)))? {
|
||||||
|
let _ = result.map_err(|e| CatalogError::Database(format!("Failed to get entry from installed table: {}", e)))?;
|
||||||
|
installed_count += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print statistics
|
||||||
|
println!("Database path: {}", self.db_path.display());
|
||||||
|
println!("Catalog directory: {}", self.catalog_dir.display());
|
||||||
|
println!("Table statistics:");
|
||||||
|
println!(" Catalog table: {} entries", catalog_count);
|
||||||
|
println!(" Obsoleted table: {} entries", obsoleted_count);
|
||||||
|
println!(" Installed table: {} entries", installed_count);
|
||||||
|
println!("Total entries: {}", catalog_count + obsoleted_count + installed_count);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
/// Initialize the catalog database
|
/// Initialize the catalog database
|
||||||
pub fn init_db(&self) -> Result<()> {
|
pub fn init_db(&self) -> Result<()> {
|
||||||
// Create a parent directory if it doesn't exist
|
// Create a parent directory if it doesn't exist
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ use std::fs;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
use tracing::{info};
|
use tracing::{info, warn};
|
||||||
|
|
||||||
/// Table definition for the installed packages database
|
/// Table definition for the installed packages database
|
||||||
/// Key: full FMRI including publisher (pkg://publisher/stem@version)
|
/// Key: full FMRI including publisher (pkg://publisher/stem@version)
|
||||||
|
|
@ -76,6 +76,87 @@ impl InstalledPackages {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Dump the contents of the installed table to stdout for debugging
|
||||||
|
pub fn dump_installed_table(&self) -> Result<()> {
|
||||||
|
// Open the database
|
||||||
|
let db = Database::open(&self.db_path)
|
||||||
|
.map_err(|e| InstalledError::Database(format!("Failed to open database: {}", e)))?;
|
||||||
|
|
||||||
|
// Begin a read transaction
|
||||||
|
let tx = db.begin_read()
|
||||||
|
.map_err(|e| InstalledError::Database(format!("Failed to begin transaction: {}", e)))?;
|
||||||
|
|
||||||
|
// Open the installed table
|
||||||
|
match tx.open_table(INSTALLED_TABLE) {
|
||||||
|
Ok(table) => {
|
||||||
|
let mut count = 0;
|
||||||
|
for entry_result in table.iter().map_err(|e| InstalledError::Database(format!("Failed to iterate installed table: {}", e)))? {
|
||||||
|
let (key, value) = entry_result.map_err(|e| InstalledError::Database(format!("Failed to get entry from installed table: {}", e)))?;
|
||||||
|
let key_str = key.value();
|
||||||
|
|
||||||
|
// Try to deserialize the manifest
|
||||||
|
match serde_json::from_slice::<Manifest>(value.value()) {
|
||||||
|
Ok(manifest) => {
|
||||||
|
// Extract the publisher from the FMRI attribute
|
||||||
|
let publisher = manifest.attributes.iter()
|
||||||
|
.find(|attr| attr.key == "pkg.fmri")
|
||||||
|
.and_then(|attr| attr.values.get(0).cloned())
|
||||||
|
.unwrap_or_else(|| "unknown".to_string());
|
||||||
|
|
||||||
|
println!("Key: {}", key_str);
|
||||||
|
println!(" FMRI: {}", publisher);
|
||||||
|
println!(" Attributes: {}", manifest.attributes.len());
|
||||||
|
println!(" Files: {}", manifest.files.len());
|
||||||
|
println!(" Directories: {}", manifest.directories.len());
|
||||||
|
println!(" Dependencies: {}", manifest.dependencies.len());
|
||||||
|
},
|
||||||
|
Err(e) => {
|
||||||
|
println!("Key: {}", key_str);
|
||||||
|
println!(" Error deserializing manifest: {}", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
count += 1;
|
||||||
|
}
|
||||||
|
println!("Total entries in installed table: {}", count);
|
||||||
|
Ok(())
|
||||||
|
},
|
||||||
|
Err(e) => {
|
||||||
|
println!("Error opening installed table: {}", e);
|
||||||
|
Err(InstalledError::Database(format!("Failed to open installed table: {}", e)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get database statistics
|
||||||
|
pub fn get_db_stats(&self) -> Result<()> {
|
||||||
|
// Open the database
|
||||||
|
let db = Database::open(&self.db_path)
|
||||||
|
.map_err(|e| InstalledError::Database(format!("Failed to open database: {}", e)))?;
|
||||||
|
|
||||||
|
// Begin a read transaction
|
||||||
|
let tx = db.begin_read()
|
||||||
|
.map_err(|e| InstalledError::Database(format!("Failed to begin transaction: {}", e)))?;
|
||||||
|
|
||||||
|
// Get table statistics
|
||||||
|
let mut installed_count = 0;
|
||||||
|
|
||||||
|
// Count installed entries
|
||||||
|
if let Ok(table) = tx.open_table(INSTALLED_TABLE) {
|
||||||
|
for result in table.iter().map_err(|e| InstalledError::Database(format!("Failed to iterate installed table: {}", e)))? {
|
||||||
|
let _ = result.map_err(|e| InstalledError::Database(format!("Failed to get entry from installed table: {}", e)))?;
|
||||||
|
installed_count += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print statistics
|
||||||
|
println!("Database path: {}", self.db_path.display());
|
||||||
|
println!("Table statistics:");
|
||||||
|
println!(" Installed table: {} entries", installed_count);
|
||||||
|
println!("Total entries: {}", installed_count);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
/// Initialize the installed packages database
|
/// Initialize the installed packages database
|
||||||
pub fn init_db(&self) -> Result<()> {
|
pub fn init_db(&self) -> Result<()> {
|
||||||
// Create a parent directory if it doesn't exist
|
// Create a parent directory if it doesn't exist
|
||||||
|
|
|
||||||
203
pkg6/src/main.rs
203
pkg6/src/main.rs
|
|
@ -228,6 +228,7 @@ enum Commands {
|
||||||
/// List installed packages
|
/// List installed packages
|
||||||
///
|
///
|
||||||
/// The list command displays information about installed packages.
|
/// The list command displays information about installed packages.
|
||||||
|
/// By default, it lists only installed packages. Use the -a flag to list all available packages.
|
||||||
List {
|
List {
|
||||||
/// Verbose output
|
/// Verbose output
|
||||||
#[clap(short)]
|
#[clap(short)]
|
||||||
|
|
@ -237,6 +238,10 @@ enum Commands {
|
||||||
#[clap(short)]
|
#[clap(short)]
|
||||||
quiet: bool,
|
quiet: bool,
|
||||||
|
|
||||||
|
/// List all available packages, not just installed ones
|
||||||
|
#[clap(short)]
|
||||||
|
all: bool,
|
||||||
|
|
||||||
/// Output format (default: table)
|
/// Output format (default: table)
|
||||||
#[clap(short = 'o')]
|
#[clap(short = 'o')]
|
||||||
output_format: Option<String>,
|
output_format: Option<String>,
|
||||||
|
|
@ -422,6 +427,35 @@ enum Commands {
|
||||||
#[clap(short = 't', long = "type", default_value = "full")]
|
#[clap(short = 't', long = "type", default_value = "full")]
|
||||||
image_type: String,
|
image_type: String,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/// Debug database commands (hidden)
|
||||||
|
///
|
||||||
|
/// These commands are for debugging purposes only and are not part of the public API.
|
||||||
|
/// They are used to inspect the contents of the redb databases for debugging purposes.
|
||||||
|
///
|
||||||
|
/// Usage examples:
|
||||||
|
/// - Show database statistics: pkg6 debug-db --stats
|
||||||
|
/// - Dump all tables: pkg6 debug-db --dump-all
|
||||||
|
/// - Dump a specific table: pkg6 debug-db --dump-table catalog
|
||||||
|
///
|
||||||
|
/// Available tables:
|
||||||
|
/// - catalog: Contains non-obsolete packages (in catalog.redb)
|
||||||
|
/// - obsoleted: Contains obsolete packages (in catalog.redb)
|
||||||
|
/// - installed: Contains installed packages (in installed.redb)
|
||||||
|
#[clap(hide = true)]
|
||||||
|
DebugDb {
|
||||||
|
/// Show database statistics
|
||||||
|
#[clap(long)]
|
||||||
|
stats: bool,
|
||||||
|
|
||||||
|
/// Dump all tables
|
||||||
|
#[clap(long)]
|
||||||
|
dump_all: bool,
|
||||||
|
|
||||||
|
/// Dump a specific table (catalog, obsoleted, installed)
|
||||||
|
#[clap(long)]
|
||||||
|
dump_table: Option<String>,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Determines the image path to use based on the provided argument and default rules
|
/// Determines the image path to use based on the provided argument and default rules
|
||||||
|
|
@ -478,6 +512,7 @@ fn main() -> Result<()> {
|
||||||
// Print the command that was parsed
|
// Print the command that was parsed
|
||||||
match &cli.command {
|
match &cli.command {
|
||||||
Commands::Publisher { .. } => eprintln!("MAIN: Publisher command detected"),
|
Commands::Publisher { .. } => eprintln!("MAIN: Publisher command detected"),
|
||||||
|
Commands::DebugDb { .. } => eprintln!("MAIN: Debug database command detected"),
|
||||||
_ => eprintln!("MAIN: Other command detected: {:?}", cli.command),
|
_ => eprintln!("MAIN: Other command detected: {:?}", cli.command),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -580,13 +615,84 @@ fn main() -> Result<()> {
|
||||||
info!("Update completed successfully");
|
info!("Update completed successfully");
|
||||||
Ok(())
|
Ok(())
|
||||||
},
|
},
|
||||||
Commands::List { verbose, quiet, output_format, pkg_fmri_patterns } => {
|
Commands::List { verbose, quiet, all, output_format, pkg_fmri_patterns } => {
|
||||||
info!("Listing packages: {:?}", pkg_fmri_patterns);
|
info!("Listing packages: {:?}", pkg_fmri_patterns);
|
||||||
debug!("Verbose: {}", verbose);
|
debug!("Verbose: {}", verbose);
|
||||||
debug!("Quiet: {}", quiet);
|
debug!("Quiet: {}", quiet);
|
||||||
|
debug!("All packages: {}", all);
|
||||||
debug!("Output format: {:?}", output_format);
|
debug!("Output format: {:?}", output_format);
|
||||||
|
|
||||||
// Stub implementation
|
// Determine the image path using the -R argument or default rules
|
||||||
|
let image_path = determine_image_path(cli.image_path.clone());
|
||||||
|
info!("Using image at: {}", image_path.display());
|
||||||
|
|
||||||
|
// Try to load the image from the determined path
|
||||||
|
let image = match libips::image::Image::load(&image_path) {
|
||||||
|
Ok(img) => img,
|
||||||
|
Err(e) => {
|
||||||
|
error!("Failed to load image from {}: {}", image_path.display(), e);
|
||||||
|
error!("Make sure the path points to a valid image or use pkg6 image-create first");
|
||||||
|
return Err(e.into());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Convert pkg_fmri_patterns to a single pattern if provided
|
||||||
|
let pattern = if pkg_fmri_patterns.is_empty() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
// For simplicity, we'll just use the first pattern
|
||||||
|
// In a more complete implementation, we would handle multiple patterns
|
||||||
|
Some(pkg_fmri_patterns[0].as_str())
|
||||||
|
};
|
||||||
|
|
||||||
|
if *all {
|
||||||
|
// List all available packages
|
||||||
|
info!("Listing all available packages");
|
||||||
|
|
||||||
|
match image.query_catalog(pattern) {
|
||||||
|
Ok(packages) => {
|
||||||
|
println!("PUBLISHER NAME VERSION STATE");
|
||||||
|
println!("------------------------------------------------------------------------------------------------------------------------------------------------------");
|
||||||
|
for pkg in packages {
|
||||||
|
let state = if image.is_package_installed(&pkg.fmri).unwrap_or(false) {
|
||||||
|
"installed"
|
||||||
|
} else {
|
||||||
|
"known"
|
||||||
|
};
|
||||||
|
println!("{:<40} {:<40} {:<30} {}",
|
||||||
|
pkg.fmri.publisher.as_deref().unwrap_or("unknown"),
|
||||||
|
pkg.fmri.name,
|
||||||
|
pkg.fmri.version(),
|
||||||
|
state);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Err(e) => {
|
||||||
|
error!("Failed to query catalog: {}", e);
|
||||||
|
return Err(e.into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// List only installed packages
|
||||||
|
info!("Listing installed packages");
|
||||||
|
match image.query_installed_packages(pattern) {
|
||||||
|
Ok(packages) => {
|
||||||
|
println!("PUBLISHER NAME VERSION STATE");
|
||||||
|
println!("------------------------------------------------------------------------------------------------------------------------------------------------------");
|
||||||
|
for pkg in packages {
|
||||||
|
println!("{:<40} {:<40} {:<30} {}",
|
||||||
|
pkg.fmri.publisher.as_deref().unwrap_or("unknown"),
|
||||||
|
pkg.fmri.name,
|
||||||
|
pkg.fmri.version(),
|
||||||
|
"installed");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Err(e) => {
|
||||||
|
error!("Failed to query installed packages: {}", e);
|
||||||
|
return Err(e.into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
info!("List completed successfully");
|
info!("List completed successfully");
|
||||||
Ok(())
|
Ok(())
|
||||||
},
|
},
|
||||||
|
|
@ -906,5 +1012,98 @@ fn main() -> Result<()> {
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
},
|
},
|
||||||
|
Commands::DebugDb { stats, dump_all, dump_table } => {
|
||||||
|
info!("Debug database command");
|
||||||
|
debug!("Stats: {}", stats);
|
||||||
|
debug!("Dump all: {}", dump_all);
|
||||||
|
debug!("Dump table: {:?}", dump_table);
|
||||||
|
|
||||||
|
// Determine the image path using the -R argument or default rules
|
||||||
|
let image_path = determine_image_path(cli.image_path.clone());
|
||||||
|
info!("Using image at: {}", image_path.display());
|
||||||
|
|
||||||
|
// Try to load the image from the determined path
|
||||||
|
let image = match libips::image::Image::load(&image_path) {
|
||||||
|
Ok(img) => img,
|
||||||
|
Err(e) => {
|
||||||
|
error!("Failed to load image from {}: {}", image_path.display(), e);
|
||||||
|
error!("Make sure the path points to a valid image or use pkg6 image-create first");
|
||||||
|
return Err(e.into());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Create a catalog object for the catalog.redb database
|
||||||
|
let catalog = libips::image::catalog::ImageCatalog::new(
|
||||||
|
image.catalog_dir(),
|
||||||
|
image.catalog_db_path()
|
||||||
|
);
|
||||||
|
|
||||||
|
// Create an installed packages object for the installed.redb database
|
||||||
|
let installed = libips::image::installed::InstalledPackages::new(
|
||||||
|
image.installed_db_path()
|
||||||
|
);
|
||||||
|
|
||||||
|
// Execute the requested debug command
|
||||||
|
if *stats {
|
||||||
|
info!("Showing database statistics");
|
||||||
|
println!("=== CATALOG DATABASE ===");
|
||||||
|
if let Err(e) = catalog.get_db_stats() {
|
||||||
|
error!("Failed to get catalog database statistics: {}", e);
|
||||||
|
return Err(Pkg6Error::Other(format!("Failed to get catalog database statistics: {}", e)));
|
||||||
|
}
|
||||||
|
|
||||||
|
println!("\n=== INSTALLED DATABASE ===");
|
||||||
|
if let Err(e) = installed.get_db_stats() {
|
||||||
|
error!("Failed to get installed database statistics: {}", e);
|
||||||
|
return Err(Pkg6Error::Other(format!("Failed to get installed database statistics: {}", e)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if *dump_all {
|
||||||
|
info!("Dumping all tables");
|
||||||
|
println!("=== CATALOG DATABASE ===");
|
||||||
|
if let Err(e) = catalog.dump_all_tables() {
|
||||||
|
error!("Failed to dump catalog database tables: {}", e);
|
||||||
|
return Err(Pkg6Error::Other(format!("Failed to dump catalog database tables: {}", e)));
|
||||||
|
}
|
||||||
|
|
||||||
|
println!("\n=== INSTALLED DATABASE ===");
|
||||||
|
if let Err(e) = installed.dump_installed_table() {
|
||||||
|
error!("Failed to dump installed database table: {}", e);
|
||||||
|
return Err(Pkg6Error::Other(format!("Failed to dump installed database table: {}", e)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(table_name) = dump_table {
|
||||||
|
info!("Dumping table: {}", table_name);
|
||||||
|
|
||||||
|
// Determine which database to use based on the table name
|
||||||
|
match table_name.as_str() {
|
||||||
|
"installed" => {
|
||||||
|
// Use the installed packages database
|
||||||
|
println!("=== INSTALLED DATABASE ===");
|
||||||
|
if let Err(e) = installed.dump_installed_table() {
|
||||||
|
error!("Failed to dump installed table: {}", e);
|
||||||
|
return Err(Pkg6Error::Other(format!("Failed to dump installed table: {}", e)));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"catalog" | "obsoleted" => {
|
||||||
|
// Use the catalog database
|
||||||
|
println!("=== CATALOG DATABASE ===");
|
||||||
|
if let Err(e) = catalog.dump_table(table_name) {
|
||||||
|
error!("Failed to dump table {}: {}", table_name, e);
|
||||||
|
return Err(Pkg6Error::Other(format!("Failed to dump table {}: {}", table_name, e)));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_ => {
|
||||||
|
error!("Unknown table: {}", table_name);
|
||||||
|
return Err(Pkg6Error::Other(format!("Unknown table: {}. Available tables: catalog, obsoleted, installed", table_name)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
info!("Debug database command completed successfully");
|
||||||
|
Ok(())
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue