provenance: convert from enum to freeform string
The Provenance enum couldn't represent agents defined outside the source code. Replace it with a Text field in the capnp schema so any agent can write its own provenance label (e.g. "extractor:write", "rename:tombstone") without a code change. Schema: rename old enum fields to provenanceOld, add new Text provenance fields. Old enum kept for reading legacy records. Migration: from_capnp_migrate() falls back to old enum when the new text field is empty. Also adds `poc-memory tail` command for viewing recent store writes. Co-Authored-By: ProofOfConcept <poc@bcachefs.org>
This commit is contained in:
parent
de204e3075
commit
d76b14dfcd
14 changed files with 160 additions and 67 deletions
|
|
@ -366,6 +366,15 @@ enum Command {
|
|||
/// Node key
|
||||
key: Vec<String>,
|
||||
},
|
||||
/// Show most recent writes to the node log
|
||||
Tail {
|
||||
/// Number of entries (default: 20)
|
||||
#[arg(default_value_t = 20)]
|
||||
n: usize,
|
||||
/// Show full content
|
||||
#[arg(long)]
|
||||
full: bool,
|
||||
},
|
||||
/// Upsert node content from stdin
|
||||
Write {
|
||||
/// Node key
|
||||
|
|
@ -567,6 +576,8 @@ fn main() {
|
|||
Command::Render { key } => cmd_render(&key),
|
||||
Command::History { full, key }
|
||||
=> cmd_history(&key, full),
|
||||
Command::Tail { n, full }
|
||||
=> cmd_tail(n, full),
|
||||
Command::Write { key } => cmd_write(&key),
|
||||
Command::Import { files }
|
||||
=> cmd_import(&files),
|
||||
|
|
@ -2017,10 +2028,10 @@ fn cmd_render(key: &[String]) -> Result<(), String> {
|
|||
}
|
||||
let key = key.join(" ");
|
||||
let store = store::Store::load()?;
|
||||
let resolved = store.resolve_key(&key)?;
|
||||
let bare = store::strip_md_suffix(&key);
|
||||
|
||||
let node = store.nodes.get(&resolved)
|
||||
.ok_or_else(|| format!("Node not found: {}", resolved))?;
|
||||
let node = store.nodes.get(&bare)
|
||||
.ok_or_else(|| format!("Node not found: {}", bare))?;
|
||||
|
||||
print!("{}", node.content);
|
||||
Ok(())
|
||||
|
|
@ -2052,7 +2063,7 @@ fn cmd_history(key: &[String], full: bool) -> Result<(), String> {
|
|||
.map_err(|e| format!("read log: {}", e))?;
|
||||
for node_reader in log.get_nodes()
|
||||
.map_err(|e| format!("get nodes: {}", e))? {
|
||||
let node = store::Node::from_capnp(node_reader)?;
|
||||
let node = store::Node::from_capnp_migrate(node_reader)?;
|
||||
if node.key == key {
|
||||
versions.push(node);
|
||||
}
|
||||
|
|
@ -2073,13 +2084,13 @@ fn cmd_history(key: &[String], full: bool) -> Result<(), String> {
|
|||
let content_len = node.content.len();
|
||||
if full {
|
||||
eprintln!("=== v{} {} {} w={:.3} {}b ===",
|
||||
node.version, ts, node.provenance.label(), node.weight, content_len);
|
||||
node.version, ts, node.provenance, node.weight, content_len);
|
||||
eprintln!("{}", node.content);
|
||||
} else {
|
||||
let preview = util::first_n_chars(&node.content, 120);
|
||||
let preview = preview.replace('\n', "\\n");
|
||||
eprintln!(" v{:<3} {} {:24} w={:.3} {}b",
|
||||
node.version, ts, node.provenance.label(), node.weight, content_len);
|
||||
node.version, ts, node.provenance, node.weight, content_len);
|
||||
eprintln!(" {}", preview);
|
||||
}
|
||||
}
|
||||
|
|
@ -2087,7 +2098,7 @@ fn cmd_history(key: &[String], full: bool) -> Result<(), String> {
|
|||
if !full {
|
||||
if let Some(latest) = versions.last() {
|
||||
eprintln!("\n--- Latest content (v{}, {}) ---",
|
||||
latest.version, latest.provenance.label());
|
||||
latest.version, latest.provenance);
|
||||
print!("{}", latest.content);
|
||||
}
|
||||
}
|
||||
|
|
@ -2095,6 +2106,56 @@ fn cmd_history(key: &[String], full: bool) -> Result<(), String> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn cmd_tail(n: usize, full: bool) -> Result<(), String> {
|
||||
let path = 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<store::Node> = Vec::new();
|
||||
while let Ok(msg) = capnp::serialize::read_message(&mut reader, capnp::message::ReaderOptions::new()) {
|
||||
let log = msg.get_root::<poc_memory::memory_capnp::node_log::Reader>()
|
||||
.map_err(|e| format!("read log: {}", e))?;
|
||||
for node_reader in log.get_nodes()
|
||||
.map_err(|e| format!("get nodes: {}", e))? {
|
||||
let node = 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 {
|
||||
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 = 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(())
|
||||
}
|
||||
|
||||
fn cmd_write(key: &[String]) -> Result<(), String> {
|
||||
if key.is_empty() {
|
||||
return Err("write requires a key (reads content from stdin)".into());
|
||||
|
|
@ -2218,7 +2279,7 @@ fn cmd_journal_write(text: &[String]) -> Result<(), String> {
|
|||
|
||||
let mut node = store::new_node(&key, &content);
|
||||
node.node_type = store::NodeType::EpisodicSession;
|
||||
node.provenance = store::Provenance::Journal;
|
||||
node.provenance = "journal".to_string();
|
||||
if let Some(src) = source_ref {
|
||||
node.source_ref = src;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue