// Append-only Cap'n Proto storage + redb indices // // capnp logs are the source of truth: // nodes.capnp - ContentNode messages // relations.capnp - Relation messages // // redb provides indexed access; Store struct holds in-memory state. // // Module layout: // types.rs — Node, Relation, enums, capnp macros, path helpers // view.rs — StoreView trait for read-only access // persist.rs — load, replay, append (capnp IO) // ops.rs — mutations (upsert, delete, decay, cap_degree, etc.) // mod.rs — re-exports, key resolution, ingestion, rendering mod types; mod view; mod persist; mod ops; pub mod db; // Re-export everything callers need pub use types::{ memory_dir, nodes_path, now_epoch, epoch_to_local, format_date, format_datetime, format_datetime_space, compact_timestamp, today, Node, Relation, NodeType, RelationType, Store, new_node, new_relation, }; pub use view::StoreView; pub use persist::fsck; pub use ops::current_provenance; use crate::graph::{self, Graph}; use anyhow::{bail, Result}; /// Strip .md suffix from a key, handling both bare keys and section keys. /// "identity.md" → "identity", "foo.md#section" → "foo#section", "identity" → "identity" pub fn strip_md_suffix(key: &str) -> String { if let Some((file, section)) = key.split_once('#') { let bare = file.strip_suffix(".md").unwrap_or(file); format!("{}#{}", bare, section) } else { key.strip_suffix(".md").unwrap_or(key).to_string() } } impl Store { pub fn build_graph(&self) -> Graph { graph::build_graph(self) } pub fn resolve_key(&self, target: &str) -> Result { // Strip .md suffix if present — keys no longer use it let bare = strip_md_suffix(target); if self.nodes.contains_key(&bare) { return Ok(bare); } let matches: Vec<_> = self.nodes.keys() .filter(|k| k.to_lowercase().contains(&target.to_lowercase())) .cloned().collect(); match matches.len() { 0 => bail!("No entry for '{}'. Run 'init'?", target), 1 => Ok(matches[0].clone()), n if n <= 10 => { let list = matches.join("\n "); bail!("Ambiguous '{}'. Matches:\n {}", target, list) } n => bail!("Too many matches for '{}' ({}). Be more specific.", target, n), } } }