use anyhow::{Context, Result};
use trusty_common::monitor::dashboard::{MemoryData, PalaceRow, UNKNOWN_COUNT};
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()
}
fn fmt_opt_count(n: Option<u64>) -> String {
match n {
Some(n) => fmt_count(n),
None => UNKNOWN_COUNT.to_string(),
}
}
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<()> {
match id {
Some(id) => {
let row = fetch_palace(&id).await?;
print_palace_detail(&row, json);
Ok(())
}
None => {
let data = fetch_memory_data().await?;
print_palace_table(&data.palaces, json);
Ok(())
}
}
}
async fn fetch_palace(id: &str) -> Result<PalaceRow> {
let url = resolve_memory_url();
let client = MemoryClient::new(url.clone());
client
.fetch_palace(id)
.await
.with_context(|| format!("could not read palace '{id}' from trusty-memory at {url}"))
}
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.vectors(),
})
})
.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_opt_count(p.vectors()),
);
}
}
fn print_palace_detail(row: &PalaceRow, json: bool) {
if json {
println!(
"{}",
serde_json::json!({
"id": row.id,
"name": row.name,
"vectors": row.vectors(),
})
);
} else {
println!("id: {}", row.id);
println!("name: {}", row.name);
println!("vectors: {}", fmt_opt_count(row.vectors()));
}
}
#[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 fmt_opt_count_marks_unknown() {
assert_eq!(fmt_opt_count(None), UNKNOWN_COUNT);
assert_ne!(fmt_opt_count(None), "0");
assert_eq!(fmt_opt_count(Some(0)), "0");
assert_eq!(fmt_opt_count(Some(8_400)), "8,400");
}
#[test]
fn print_palace_detail_renders_unknown_vectors() {
let cold = PalaceRow {
id: "t-tmpugxp9v".into(),
name: "t-tmpugxp9v".into(),
vector_count: 0,
counts_unknown: true,
..Default::default()
};
assert_eq!(cold.vectors(), None);
assert_eq!(fmt_opt_count(cold.vectors()), UNKNOWN_COUNT);
let warm = PalaceRow {
id: "t-tmpugxp9v".into(),
name: "t-tmpugxp9v".into(),
vector_count: 912,
..Default::default()
};
assert_eq!(warm.vectors(), Some(912));
assert_eq!(fmt_opt_count(warm.vectors()), "912");
print_palace_detail(&cold, false);
print_palace_detail(&cold, true);
print_palace_detail(&warm, true);
}
}