// cli/journal.rs — journal subcommand handlers use crate::store; use crate::store::StoreView; pub fn cmd_tail(n: usize, full: bool) -> Result<(), String> { let path = crate::store::nodes_path(); if !path.exists() { return Err("No node log found".into()); } use std::io::BufReader; let file = std::fs::File::open(&path) .map_err(|e| format!("open {}: {}", path.display(), e))?; let mut reader = BufReader::new(file); // Read all entries, keep last N let mut entries: Vec = Vec::new(); while let Ok(msg) = capnp::serialize::read_message(&mut reader, capnp::message::ReaderOptions::new()) { let log = msg.get_root::() .map_err(|e| format!("read log: {}", e))?; for node_reader in log.get_nodes() .map_err(|e| format!("get nodes: {}", e))? { let node = crate::store::Node::from_capnp_migrate(node_reader)?; entries.push(node); } } let start = entries.len().saturating_sub(n); for node in &entries[start..] { let ts = if node.timestamp > 0 && node.timestamp < 4_000_000_000 { crate::store::format_datetime(node.timestamp) } else { format!("(raw:{})", node.timestamp) }; let del = if node.deleted { " [DELETED]" } else { "" }; if full { eprintln!("--- {} (v{}) {} via {} w={:.3}{} ---", node.key, node.version, ts, node.provenance, node.weight, del); eprintln!("{}\n", node.content); } else { let preview = crate::util::first_n_chars(&node.content, 100).replace('\n', "\\n"); eprintln!(" {} v{} w={:.2}{}", ts, node.version, node.weight, del); eprintln!(" {} via {}", node.key, node.provenance); if !preview.is_empty() { eprintln!(" {}", preview); } eprintln!(); } } Ok(()) }