use anyhow::{bail, Result};
use trusty_common::monitor::dashboard::{MemoryData, PalaceRow};
use trusty_common::monitor::memory_client::{resolve_memory_url, MemoryClient};
fn fmt_count(n: u64) -> String {
let s = n.to_string();
let mut out = String::with_capacity(s.len() + s.len() / 3);
for (i, c) in s.chars().rev().enumerate() {
if i > 0 && i % 3 == 0 {
out.push(',');
}
out.push(c);
}
out.chars().rev().collect()
}
async fn fetch_memory_data() -> Result<MemoryData> {
let url = resolve_memory_url();
let client = MemoryClient::new(url.clone());
client
.fetch_all()
.await
.map_err(|e| anyhow::anyhow!("could not reach trusty-memory daemon at {url}: {e}"))
}
pub async fn handle_status(json: bool) -> Result<()> {
let data = fetch_memory_data().await?;
if json {
println!(
"{}",
serde_json::json!({
"status": "online",
"version": data.version,
"palace_count": data.palace_count,
"total_drawers": data.total_drawers,
"total_vectors": data.total_vectors,
"total_kg_triples": data.total_kg_triples,
})
);
} else {
println!("trusty-memory v{}", data.version);
println!("status: online");
println!("palaces: {}", data.palace_count);
println!("drawers: {}", fmt_count(data.total_drawers));
println!("vectors: {}", fmt_count(data.total_vectors));
println!("kg triples: {}", fmt_count(data.total_kg_triples));
}
Ok(())
}
pub async fn handle_palaces(id: Option<String>, json: bool) -> Result<()> {
let data = fetch_memory_data().await?;
match id {
Some(id) => print_palace_detail(&data.palaces, &id, json),
None => {
print_palace_table(&data.palaces, json);
Ok(())
}
}
}
fn print_palace_table(palaces: &[PalaceRow], json: bool) {
if json {
let arr: Vec<serde_json::Value> = palaces
.iter()
.map(|p| {
serde_json::json!({
"id": p.id,
"name": p.name,
"vectors": p.vector_count,
})
})
.collect();
println!("{}", serde_json::Value::Array(arr));
return;
}
if palaces.is_empty() {
println!("(no palaces)");
return;
}
let id_w = palaces
.iter()
.map(|p| p.id.len())
.max()
.unwrap_or(0)
.max(12);
let name_w = palaces
.iter()
.map(|p| p.name.len())
.max()
.unwrap_or(0)
.max(12);
println!("{:<id_w$} {:<name_w$} VECTORS", "ID", "NAME");
for p in palaces {
println!(
"{:<id_w$} {:<name_w$} {}",
p.id,
p.name,
fmt_count(p.vector_count),
);
}
}
fn print_palace_detail(palaces: &[PalaceRow], id: &str, json: bool) -> Result<()> {
let Some(row) = palaces.iter().find(|p| p.id == id) else {
bail!("no palace named '{id}' is registered");
};
if json {
println!(
"{}",
serde_json::json!({
"id": row.id,
"name": row.name,
"vectors": row.vector_count,
})
);
} else {
println!("id: {}", row.id);
println!("name: {}", row.name);
println!("vectors: {}", fmt_count(row.vector_count));
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn fmt_count_groups_thousands() {
assert_eq!(fmt_count(0), "0");
assert_eq!(fmt_count(42), "42");
assert_eq!(fmt_count(8_400), "8,400");
assert_eq!(fmt_count(1_234_567), "1,234,567");
}
#[test]
fn print_palace_detail_errors_on_unknown_id() {
let rows = vec![PalaceRow {
id: "default".into(),
name: "default".into(),
vector_count: 8_400,
}];
assert!(print_palace_detail(&rows, "missing", false).is_err());
assert!(print_palace_detail(&rows, "default", true).is_ok());
}
}