use anyhow::{Context, Result};
use rusqlite::params;
use crate::export::{ExportedSource, load_source};
use crate::inspect::{ago, now_unix};
use crate::store::Store;
pub fn show(store: &Store, id_or_prefix: &str) -> Result<ExportedSource> {
let trimmed = id_or_prefix.trim();
if trimmed.is_empty() {
anyhow::bail!("source id must not be empty");
}
let conn = store.conn();
let candidates: Vec<String> = {
let mut stmt =
conn.prepare("SELECT id FROM sources WHERE id LIKE ?1 || '%' ORDER BY id LIMIT 2")?;
let rows = stmt.query_map(params![trimmed], |row| row.get::<_, String>(0))?;
rows.collect::<Result<Vec<_>, _>>()?
};
match candidates.len() {
0 => anyhow::bail!("no source matches id {trimmed:?}"),
1 => load_source(conn, &candidates[0])
.with_context(|| format!("loading source {}", candidates[0])),
_ => anyhow::bail!(
"source id {trimmed:?} is ambiguous; try a longer prefix (matched at least {} sources)",
candidates.len()
),
}
}
fn chunk_metadata_line(chunk: &crate::export::ExportedChunk) -> Option<String> {
let mut parts = Vec::new();
if let Some(role) = &chunk.role {
parts.push(format!("role={role}"));
}
if let Some(session_id) = &chunk.session_id {
parts.push(format!("session={session_id}"));
}
if let Some(turn_id) = &chunk.turn_id {
parts.push(format!("turn={turn_id}"));
}
if let Some(tool_name) = &chunk.tool_name {
parts.push(format!("tool={tool_name}"));
}
if let Some(ts) = chunk.timestamp_unix {
parts.push(format!("ts={ts}"));
}
if parts.is_empty() {
None
} else {
Some(parts.join(" "))
}
}
pub fn print_text(source: &ExportedSource) {
println!("source: {}", source.source_id);
println!("uri: {}", source.uri);
if let Some(p) = &source.path {
println!("path: {p}");
}
println!("kind: {}", source.kind);
println!("bytes: {}", source.bytes);
println!("sha256: {}", source.content_sha256);
let now = now_unix();
println!(
"ingested: {} ({})",
source.ingested_at,
ago(now, source.ingested_at)
);
if let Some(m) = source.mtime_unix {
println!("mtime: {m} ({})", ago(now, m));
}
println!("chunks: {}", source.chunks.len());
for chunk in &source.chunks {
println!();
println!(
"--- chunk {ord} [bytes {start}..{end}, chars {chars}, sha {sha}] ---",
ord = chunk.ordinal,
start = chunk.byte_start,
end = chunk.byte_end,
chars = chunk.char_count,
sha = &chunk.sha256[..12.min(chunk.sha256.len())],
);
if let Some(meta) = chunk_metadata_line(chunk) {
println!("{meta}");
}
print!("{}", chunk.text);
if !chunk.text.ends_with('\n') {
println!();
}
}
}
pub fn print_json(source: &ExportedSource) -> Result<()> {
println!("{}", serde_json::to_string_pretty(source)?);
Ok(())
}