use std::fmt;
use anyhow::Result;
use clap::{Args, Subcommand};
use io_replica::collection::ReplicaCollectionId;
use pimalaya_cli::{
printer::Printer,
table::{Cell, ContentArrangement, Table, presets::UTF8_FULL},
};
use serde::Serialize;
use crate::cli::{StoreFlags, bytes, report};
#[derive(Debug, Subcommand)]
pub enum StoreCommand {
Info(StoreInfoCommand),
}
impl StoreCommand {
pub fn execute(self, printer: &mut impl Printer, store: &StoreFlags) -> Result<()> {
match self {
Self::Info(cmd) => cmd.execute(printer, store),
}
}
}
#[derive(Debug, Args)]
pub struct StoreInfoCommand;
impl StoreInfoCommand {
pub fn execute(self, printer: &mut impl Printer, store: &StoreFlags) -> Result<()> {
let path = store.dir().display().to_string();
let read = store.read()?;
let mut collections = Vec::new();
let mut live = 0;
let mut retained = 0;
for collection in read.list_collections().map_err(report)? {
let id = ReplicaCollectionId(collection.id.clone());
let collection_live = read.count_items(&collection.id).map_err(report)?;
let collection_retained = read.count_retained(&id).map_err(report)?.max(0) as u64;
live += collection_live;
retained += collection_retained;
collections.push(StoreCollectionCount {
id: collection.id,
live: collection_live,
retained: collection_retained,
});
}
let objects = read.object_stats().map_err(report)?;
let live_bytes = read.live_bytes().map_err(report)?;
let retained_bytes = read.retained_bytes().map_err(report)?;
printer.out(StoreInfoOutput {
path,
schema_version: io_pimdir::sql::VERSION,
supported_schema_version: io_pimdir::sql::VERSION,
sources: read.distinct_sources().map_err(report)?,
collections,
live_items: live,
retained_items: retained,
objects: objects.count,
object_bytes: objects.bytes,
live_bytes,
retained_bytes,
})
}
}
#[derive(Debug, Serialize)]
pub struct StoreCollectionCount {
pub id: String,
pub live: u64,
pub retained: u64,
}
#[derive(Debug, Serialize)]
pub struct StoreInfoOutput {
pub path: String,
pub schema_version: i64,
pub supported_schema_version: i64,
pub sources: Vec<String>,
pub collections: Vec<StoreCollectionCount>,
pub live_items: u64,
pub retained_items: u64,
pub objects: u64,
pub object_bytes: u64,
pub live_bytes: u64,
pub retained_bytes: u64,
}
impl fmt::Display for StoreInfoOutput {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
writeln!(f, "Store at {}", self.path)?;
writeln!(
f,
" - schema version: {} (this build services {})",
self.schema_version, self.supported_schema_version
)?;
writeln!(
f,
" - sources: {}",
if self.sources.is_empty() {
String::from("none yet")
} else {
self.sources.join(", ")
}
)?;
writeln!(
f,
" - items: {} live, {} retained",
self.live_items, self.retained_items
)?;
writeln!(
f,
" - objects: {} ({} total, {} live, {} retained)",
self.objects,
bytes(self.object_bytes),
bytes(self.live_bytes),
bytes(self.retained_bytes)
)?;
if self.collections.is_empty() {
return writeln!(f, " - collections: none yet");
}
writeln!(f)?;
let mut table = Table::new();
table
.load_style(UTF8_FULL)
.set_content_arrangement(ContentArrangement::Dynamic)
.set_header(vec![
Cell::new("COLLECTION"),
Cell::new("LIVE"),
Cell::new("RETAINED"),
]);
for collection in &self.collections {
table.add_row(vec![
Cell::new(&collection.id),
Cell::new(collection.live),
Cell::new(collection.retained),
]);
}
writeln!(f, "{table}")
}
}