use alloc::{format, string::String, vec::Vec};
use std::collections::BTreeSet;
use rusqlite::{OptionalExtension, named_params};
use serde::Serialize;
use crate::{
client::{PimdirError, reader::PimdirReader, rows},
sql,
};
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Serialize)]
pub struct PimdirObjectStats {
pub count: u64,
pub bytes: u64,
}
#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
pub struct PimdirRefcountDrift {
pub hash: String,
pub stored: i64,
pub expected: i64,
}
#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
pub struct PimdirMinted {
pub collection: String,
pub items: i64,
}
#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
pub struct PimdirDangling {
pub kind: &'static str,
pub row: String,
pub target: String,
}
impl PimdirReader {
pub fn object_stats(&self) -> Result<PimdirObjectStats, PimdirError> {
let (count, bytes) = self.conn.query_row(sql::OBJECT_STATS, [], |r| {
Ok((r.get::<_, i64>(0)?, r.get::<_, i64>(1)?))
})?;
Ok(PimdirObjectStats {
count: count.max(0) as u64,
bytes: bytes.max(0) as u64,
})
}
pub fn live_bytes(&self) -> Result<u64, PimdirError> {
let bytes: i64 = self.conn.query_row(sql::LIVE_BYTES, [], |r| r.get(0))?;
Ok(bytes.max(0) as u64)
}
pub fn object_size(&self, hash: &str) -> Result<Option<u64>, PimdirError> {
let size: Option<i64> = self
.conn
.query_row(sql::OBJECT_SIZE, named_params! { ":hash": hash }, |r| {
r.get(0)
})
.optional()?;
Ok(size.map(|size| size.max(0) as u64))
}
pub fn retained_before(&self, cutoff: &str) -> Result<(u64, u64), PimdirError> {
let (count, bytes) = self.conn.query_row(
sql::COUNT_RETAINED_BEFORE,
named_params! { ":cutoff": cutoff },
|r| Ok((r.get::<_, i64>(0)?, r.get::<_, i64>(1)?)),
)?;
Ok((count.max(0) as u64, bytes.max(0) as u64))
}
pub fn indexed_hashes(&self) -> Result<BTreeSet<String>, PimdirError> {
Ok(rows(&self.conn, sql::LIST_OBJECT_HASHES, [], |r| r.get(0))?
.into_iter()
.collect())
}
pub fn refcount_drift(&self) -> Result<Vec<PimdirRefcountDrift>, PimdirError> {
Ok(rows(&self.conn, sql::REFCOUNT_DRIFT, [], |r| {
Ok(PimdirRefcountDrift {
hash: r.get(0)?,
stored: r.get(1)?,
expected: r.get(2)?,
})
})?)
}
pub fn minted_keys(&self) -> Result<Vec<PimdirMinted>, PimdirError> {
Ok(rows(&self.conn, sql::MINTED_KEYS, [], |r| {
Ok(PimdirMinted {
collection: r.get(0)?,
items: r.get(1)?,
})
})?)
}
pub fn dangling(&self) -> Result<Vec<PimdirDangling>, PimdirError> {
let mut dangling = rows(&self.conn, sql::DANGLING_BINDINGS, [], |r| {
Ok(PimdirDangling {
kind: "binding",
row: format!(
"{}/{} @{}",
r.get::<_, String>(0)?,
r.get::<_, String>(1)?,
r.get::<_, String>(2)?
),
target: format!("item {}/{}", r.get::<_, String>(0)?, r.get::<_, String>(1)?),
})
})?;
dangling.extend(rows(&self.conn, sql::DANGLING_ITEM_OBJECTS, [], |r| {
Ok(PimdirDangling {
kind: "item-object",
row: format!("{}/{}", r.get::<_, String>(0)?, r.get::<_, String>(1)?),
target: format!("object {}", r.get::<_, String>(2)?),
})
})?);
dangling.extend(rows(&self.conn, sql::DANGLING_QUEUE_OBJECTS, [], |r| {
Ok(PimdirDangling {
kind: "queue-object",
row: format!("queue {} ({})", r.get::<_, i64>(0)?, r.get::<_, String>(1)?),
target: format!("object {}", r.get::<_, String>(2)?),
})
})?);
Ok(dangling)
}
}