Restructure store module with clearer file names: - persist.rs → capnp.rs (capnp log IO) - db.rs → index.rs (redb index operations) redb now stores key → offset mapping, not serialized nodes. Mutations record the offset after appending to capnp log. rebuild_index scans capnp log to reconstruct the index. The HashMap still exists for now; next step is to use the index for lookups and remove it. Co-Authored-By: Proof of Concept <poc@bcachefs.org>
75 lines
2.3 KiB
Rust
75 lines
2.3 KiB
Rust
// 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
|
|
// index.rs — redb index operations
|
|
// capnp.rs — capnp log IO (load, replay, append, fsck)
|
|
// ops.rs — mutations (upsert, delete, rename, etc.)
|
|
// view.rs — StoreView trait for read-only access
|
|
|
|
mod types;
|
|
mod index;
|
|
mod capnp;
|
|
mod ops;
|
|
mod view;
|
|
|
|
// 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 capnp::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<String> {
|
|
// 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),
|
|
}
|
|
}
|
|
}
|