refactor: extract Store methods, clean up shell-outs
- Add Store::upsert() — generic create-or-update, used by cmd_write - Add Store::insert_node() — for pre-constructed nodes (journal entries) - Add Store::delete_node() — soft-delete with version bump - Simplify cmd_write (20 → 8 lines), cmd_node_delete (16 → 7 lines), cmd_journal_write (removes manual append/insert/save boilerplate) - Replace generate_cookie shell-out to head/urandom with direct /dev/urandom read + const alphabet table main.rs: 1137 → 1109 lines.
This commit is contained in:
parent
29d5ed47a1
commit
0ea86b8d54
3 changed files with 62 additions and 55 deletions
|
|
@ -150,19 +150,12 @@ fn load_or_create_cookie(dir: &Path, session_id: &str) -> String {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn generate_cookie() -> String {
|
fn generate_cookie() -> String {
|
||||||
let out = Command::new("head")
|
const ALPHA: &[u8] = b"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||||
.args(["-c", "12", "/dev/urandom"])
|
let mut buf = [0u8; 16];
|
||||||
.output()
|
fs::File::open("/dev/urandom")
|
||||||
|
.and_then(|mut f| io::Read::read_exact(&mut f, &mut buf))
|
||||||
.expect("failed to read urandom");
|
.expect("failed to read urandom");
|
||||||
out.stdout.iter()
|
buf.iter().map(|b| ALPHA[(*b as usize) % ALPHA.len()] as char).collect()
|
||||||
.map(|b| {
|
|
||||||
let idx = (*b as usize) % 62;
|
|
||||||
if idx < 10 { (b'0' + idx as u8) as char }
|
|
||||||
else if idx < 36 { (b'a' + (idx - 10) as u8) as char }
|
|
||||||
else { (b'A' + (idx - 36) as u8) as char }
|
|
||||||
})
|
|
||||||
.take(16)
|
|
||||||
.collect()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_seen(dir: &Path, session_id: &str) -> HashSet<String> {
|
fn load_seen(dir: &Path, session_id: &str) -> HashSet<String> {
|
||||||
|
|
|
||||||
|
|
@ -491,6 +491,48 @@ impl Store {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Upsert a node: update if exists (and content changed), create if not.
|
||||||
|
/// Returns: "created", "updated", or "unchanged".
|
||||||
|
pub fn upsert(&mut self, key: &str, content: &str) -> Result<&'static str, String> {
|
||||||
|
if let Some(existing) = self.nodes.get(key) {
|
||||||
|
if existing.content == content {
|
||||||
|
return Ok("unchanged");
|
||||||
|
}
|
||||||
|
let mut node = existing.clone();
|
||||||
|
node.content = content.to_string();
|
||||||
|
node.version += 1;
|
||||||
|
self.append_nodes(std::slice::from_ref(&node))?;
|
||||||
|
self.nodes.insert(key.to_string(), node);
|
||||||
|
Ok("updated")
|
||||||
|
} else {
|
||||||
|
let node = Store::new_node(key, content);
|
||||||
|
self.append_nodes(std::slice::from_ref(&node))?;
|
||||||
|
self.uuid_to_key.insert(node.uuid, node.key.clone());
|
||||||
|
self.nodes.insert(key.to_string(), node);
|
||||||
|
Ok("created")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Soft-delete a node (appends deleted version, removes from cache).
|
||||||
|
pub fn delete_node(&mut self, key: &str) -> Result<(), String> {
|
||||||
|
let node = self.nodes.get(key)
|
||||||
|
.ok_or_else(|| format!("No node '{}'", key))?;
|
||||||
|
let mut deleted = node.clone();
|
||||||
|
deleted.deleted = true;
|
||||||
|
deleted.version += 1;
|
||||||
|
self.append_nodes(std::slice::from_ref(&deleted))?;
|
||||||
|
self.nodes.remove(key);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Insert a fully constructed node (for journal entries, imports, etc.)
|
||||||
|
pub fn insert_node(&mut self, node: Node) -> Result<(), String> {
|
||||||
|
self.append_nodes(std::slice::from_ref(&node))?;
|
||||||
|
self.uuid_to_key.insert(node.uuid, node.key.clone());
|
||||||
|
self.nodes.insert(node.key.clone(), node);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
/// Create a new node with defaults
|
/// Create a new node with defaults
|
||||||
pub fn new_node(key: &str, content: &str) -> Node {
|
pub fn new_node(key: &str, content: &str) -> Node {
|
||||||
Node {
|
Node {
|
||||||
|
|
|
||||||
58
src/main.rs
58
src/main.rs
|
|
@ -793,24 +793,10 @@ fn cmd_node_delete(args: &[String]) -> Result<(), String> {
|
||||||
let key = args.join(" ");
|
let key = args.join(" ");
|
||||||
let mut store = capnp_store::Store::load()?;
|
let mut store = capnp_store::Store::load()?;
|
||||||
let resolved = store.resolve_key(&key)?;
|
let resolved = store.resolve_key(&key)?;
|
||||||
|
store.delete_node(&resolved)?;
|
||||||
let updated = if let Some(node) = store.nodes.get_mut(&resolved) {
|
store.save()?;
|
||||||
node.deleted = true;
|
println!("Deleted '{}'", resolved);
|
||||||
node.version += 1;
|
Ok(())
|
||||||
Some(node.clone())
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Some(node) = updated {
|
|
||||||
store.append_nodes(&[node])?;
|
|
||||||
store.nodes.remove(&resolved);
|
|
||||||
store.save()?;
|
|
||||||
println!("Deleted '{}'", resolved);
|
|
||||||
Ok(())
|
|
||||||
} else {
|
|
||||||
Err(format!("No node '{}'", resolved))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cmd_load_context() -> Result<(), String> {
|
fn cmd_load_context() -> Result<(), String> {
|
||||||
|
|
@ -923,27 +909,15 @@ fn cmd_write(args: &[String]) -> Result<(), String> {
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut store = capnp_store::Store::load()?;
|
let mut store = capnp_store::Store::load()?;
|
||||||
|
let result = store.upsert(&key, &content)?;
|
||||||
if let Some(existing) = store.nodes.get(&key) {
|
match result {
|
||||||
if existing.content == content {
|
"unchanged" => println!("No change: '{}'", key),
|
||||||
println!("No change: '{}'", key);
|
"updated" => println!("Updated '{}' (v{})", key, store.nodes[&key].version),
|
||||||
return Ok(());
|
_ => println!("Created '{}'", key),
|
||||||
}
|
}
|
||||||
let mut node = existing.clone();
|
if result != "unchanged" {
|
||||||
node.content = content;
|
store.save()?;
|
||||||
node.version += 1;
|
|
||||||
store.append_nodes(std::slice::from_ref(&node))?;
|
|
||||||
store.nodes.insert(key.clone(), node);
|
|
||||||
println!("Updated '{}' (v{})", key, store.nodes[&key].version);
|
|
||||||
} else {
|
|
||||||
let node = capnp_store::Store::new_node(&key, &content);
|
|
||||||
store.append_nodes(std::slice::from_ref(&node))?;
|
|
||||||
store.uuid_to_key.insert(node.uuid, node.key.clone());
|
|
||||||
store.nodes.insert(key.clone(), node);
|
|
||||||
println!("Created '{}'", key);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
store.save()?;
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1055,13 +1029,11 @@ fn cmd_journal_write(args: &[String]) -> Result<(), String> {
|
||||||
let mut node = capnp_store::Store::new_node(&key, &content);
|
let mut node = capnp_store::Store::new_node(&key, &content);
|
||||||
node.node_type = capnp_store::NodeType::EpisodicSession;
|
node.node_type = capnp_store::NodeType::EpisodicSession;
|
||||||
node.provenance = capnp_store::Provenance::Journal;
|
node.provenance = capnp_store::Provenance::Journal;
|
||||||
if let Some(ref src) = source_ref {
|
if let Some(src) = source_ref {
|
||||||
node.source_ref = src.clone();
|
node.source_ref = src;
|
||||||
}
|
}
|
||||||
|
|
||||||
store.append_nodes(&[node.clone()])?;
|
store.insert_node(node)?;
|
||||||
store.uuid_to_key.insert(node.uuid, node.key.clone());
|
|
||||||
store.nodes.insert(key.clone(), node);
|
|
||||||
store.save()?;
|
store.save()?;
|
||||||
|
|
||||||
let word_count = text.split_whitespace().count();
|
let word_count = text.split_whitespace().count();
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue