Skip to content

Commit c21da2a

Browse files
committed
libvirt: Standardize table output with comfy_table, add JSON format support
Replace manual printf-style table formatting with comfy_table for consistent, well-formatted output across all list commands. Add --format=json support to base-disks list command to match the pattern used by other list commands, enabling machine-readable output for integration and scripting. Assisted-By: Claude Code Signed-off-by: Colin Walters <[email protected]>
1 parent e4cea7b commit c21da2a

File tree

3 files changed

+76
-58
lines changed

3 files changed

+76
-58
lines changed

crates/kit/src/libvirt/base_disks.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -363,7 +363,7 @@ pub fn list_base_disks(connect_uri: Option<&String>) -> Result<Vec<BaseDiskInfo>
363363
}
364364

365365
/// Information about a base disk
366-
#[derive(Debug)]
366+
#[derive(Debug, serde::Serialize, serde::Deserialize)]
367367
pub struct BaseDiskInfo {
368368
pub path: Utf8PathBuf,
369369
pub image_digest: Option<String>,

crates/kit/src/libvirt/base_disks_cli.rs

Lines changed: 66 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
66
use clap::{Parser, Subcommand};
77
use color_eyre::Result;
8+
use comfy_table::{presets::UTF8_FULL, Table};
9+
use serde_json;
810

911
use super::base_disks::{list_base_disks, prune_base_disks};
1012

@@ -19,11 +21,19 @@ pub struct LibvirtBaseDisksOpts {
1921
#[derive(Debug, Subcommand)]
2022
pub enum BaseDisksSubcommand {
2123
/// List all base disk images
22-
List,
24+
List(ListOpts),
2325
/// Prune unreferenced base disk images
2426
Prune(PruneOpts),
2527
}
2628

29+
/// Options for list command
30+
#[derive(Debug, Parser)]
31+
pub struct ListOpts {
32+
/// Output format
33+
#[clap(long, default_value = "table")]
34+
pub format: String,
35+
}
36+
2737
/// Options for prune command
2838
#[derive(Debug, Parser)]
2939
pub struct PruneOpts {
@@ -37,59 +47,70 @@ pub fn run(global_opts: &crate::libvirt::LibvirtOptions, opts: LibvirtBaseDisksO
3747
let connect_uri = global_opts.connect.as_ref();
3848

3949
match opts.command {
40-
BaseDisksSubcommand::List => run_list(connect_uri),
50+
BaseDisksSubcommand::List(list_opts) => run_list(connect_uri, list_opts),
4151
BaseDisksSubcommand::Prune(prune_opts) => run_prune(connect_uri, prune_opts),
4252
}
4353
}
4454

4555
/// Execute the list subcommand
46-
fn run_list(connect_uri: Option<&String>) -> Result<()> {
56+
fn run_list(connect_uri: Option<&String>, opts: ListOpts) -> Result<()> {
4757
let base_disks = list_base_disks(connect_uri)?;
4858

49-
if base_disks.is_empty() {
50-
println!("No base disk images found");
51-
return Ok(());
59+
match opts.format.as_str() {
60+
"table" => {
61+
if base_disks.is_empty() {
62+
println!("No base disk images found");
63+
return Ok(());
64+
}
65+
66+
let mut table = Table::new();
67+
table.load_preset(UTF8_FULL);
68+
table.set_header(vec!["NAME", "SIZE", "REFS", "IMAGE DIGEST"]);
69+
70+
for disk in &base_disks {
71+
let name = disk.path.file_name().unwrap_or("unknown");
72+
73+
let size = disk
74+
.size
75+
.map(|bytes| indicatif::BinaryBytes(bytes).to_string())
76+
.unwrap_or_else(|| "unknown".to_string());
77+
78+
let refs = disk.ref_count.to_string();
79+
80+
let digest = disk
81+
.image_digest
82+
.as_ref()
83+
.map(|d| {
84+
// Truncate long digests for display
85+
if d.len() > 56 {
86+
format!("{}...", &d[..53])
87+
} else {
88+
d.clone()
89+
}
90+
})
91+
.unwrap_or_else(|| "<no metadata>".to_string());
92+
93+
table.add_row(vec![name, &size, &refs, &digest]);
94+
}
95+
96+
println!("{}", table);
97+
println!(
98+
"\nFound {} base disk{}",
99+
base_disks.len(),
100+
if base_disks.len() == 1 { "" } else { "s" }
101+
);
102+
}
103+
"json" => {
104+
println!("{}", serde_json::to_string_pretty(&base_disks)?);
105+
}
106+
_ => {
107+
return Err(color_eyre::eyre::eyre!(
108+
"Unsupported format: {}",
109+
opts.format
110+
))
111+
}
52112
}
53113

54-
// Print table header
55-
println!(
56-
"{:<40} {:<10} {:<10} {:<58}",
57-
"NAME", "SIZE", "REFS", "IMAGE DIGEST"
58-
);
59-
println!("{}", "=".repeat(118));
60-
61-
for disk in &base_disks {
62-
let name = disk.path.file_name().unwrap_or("unknown");
63-
64-
let size = disk
65-
.size
66-
.map(|bytes| indicatif::BinaryBytes(bytes).to_string())
67-
.unwrap_or_else(|| "unknown".to_string());
68-
69-
let refs = disk.ref_count.to_string();
70-
71-
let digest = disk
72-
.image_digest
73-
.as_ref()
74-
.map(|d| {
75-
// Truncate long digests for display
76-
if d.len() > 56 {
77-
format!("{}...", &d[..53])
78-
} else {
79-
d.clone()
80-
}
81-
})
82-
.unwrap_or_else(|| "<no metadata>".to_string());
83-
84-
println!("{:<40} {:<10} {:<10} {:<58}", name, size, refs, digest);
85-
}
86-
87-
println!(
88-
"\nFound {} base disk{}",
89-
base_disks.len(),
90-
if base_disks.len() == 1 { "" } else { "s" }
91-
);
92-
93114
Ok(())
94115
}
95116

crates/kit/src/libvirt/list.rs

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
66
use clap::Parser;
77
use color_eyre::Result;
8+
use comfy_table::{presets::UTF8_FULL, Table};
89

910
/// Options for listing libvirt domains
1011
#[derive(Debug, Parser)]
@@ -54,11 +55,11 @@ pub fn run(global_opts: &crate::libvirt::LibvirtOptions, opts: LibvirtListOpts)
5455
}
5556
return Ok(());
5657
}
57-
println!(
58-
"{:<20} {:<40} {:<12} {:<20}",
59-
"NAME", "IMAGE", "STATUS", "MEMORY"
60-
);
61-
println!("{}", "=".repeat(92));
58+
59+
let mut table = Table::new();
60+
table.load_preset(UTF8_FULL);
61+
table.set_header(vec!["NAME", "IMAGE", "STATUS", "MEMORY"]);
62+
6263
for domain in &domains {
6364
let image = match &domain.image {
6465
Some(img) => {
@@ -74,14 +75,10 @@ pub fn run(global_opts: &crate::libvirt::LibvirtOptions, opts: LibvirtListOpts)
7475
Some(mem) => format!("{}MB", mem),
7576
None => "unknown".to_string(),
7677
};
77-
println!(
78-
"{:<20} {:<40} {:<12} {:<20}",
79-
domain.name,
80-
image,
81-
domain.status_string(),
82-
memory
83-
);
78+
table.add_row(vec![&domain.name, &image, &domain.status_string(), &memory]);
8479
}
80+
81+
println!("{}", table);
8582
println!(
8683
"\nFound {} domain{} (source: libvirt)",
8784
domains.len(),

0 commit comments

Comments
 (0)