use comfy_table::{ContentArrangement, Table};
use kiromi_ai_memory::{MemoryRecord, MemoryRef, SearchHit};
use serde::Serialize;
#[must_use]
pub(crate) fn refs_table(refs: &[MemoryRef]) -> String {
let mut t = Table::new();
t.set_content_arrangement(ContentArrangement::Dynamic);
t.set_header(vec!["id", "partition"]);
for r in refs {
t.add_row(vec![r.id.to_string(), r.partition.as_str().to_string()]);
}
t.to_string()
}
#[must_use]
pub(crate) fn hits_table(hits: &[SearchHit]) -> String {
let mut t = Table::new();
t.set_content_arrangement(ContentArrangement::Dynamic);
t.set_header(vec!["score", "id", "partition"]);
for h in hits {
t.add_row(vec![
format!("{:.4}", h.score),
h.r#ref.id.to_string(),
h.r#ref.partition.as_str().to_string(),
]);
}
t.to_string()
}
#[must_use]
pub(crate) fn record_kv(r: &MemoryRecord) -> String {
let mut t = Table::new();
t.set_content_arrangement(ContentArrangement::Dynamic);
t.add_row(vec!["id".to_string(), r.r#ref.id.to_string()]);
t.add_row(vec!["partition".to_string(), r.r#ref.partition.to_string()]);
t.add_row(vec![
"created_at_ms".to_string(),
r.created_at_ms.to_string(),
]);
t.add_row(vec![
"updated_at_ms".to_string(),
r.updated_at_ms.to_string(),
]);
t.add_row(vec!["tombstoned".to_string(), r.tombstoned.to_string()]);
t.to_string()
}
#[must_use]
pub(crate) fn to_json<T: Serialize>(v: &T) -> String {
match serde_json::to_string_pretty(v) {
Ok(s) => s,
Err(_) => "null".into(),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn refs_table_handles_empty() {
let s = refs_table(&[]);
assert!(s.contains("id"));
assert!(s.contains("partition"));
}
#[test]
fn hits_table_handles_empty() {
let s = hits_table(&[]);
assert!(s.contains("score"));
}
#[test]
fn to_json_pretty_prints() {
let v = serde_json::json!({"a": 1});
let s = to_json(&v);
assert!(s.contains("\"a\""));
}
}