feat: introduce global meta store and root store concept, and update CLI to manage active store

This commit is contained in:
2025-12-21 23:59:08 +01:00
parent f45c6ccfcf
commit 346ebccee7
15 changed files with 851 additions and 283 deletions
+54 -35
View File
@@ -3,7 +3,8 @@
mod node;
mod commands;
use node::LatticeNodeBuilder;
use commands::CommandResult;
use node::{LatticeNodeBuilder, StoreHandle};
use rustyline::error::ReadlineError;
use rustyline::DefaultEditor;
@@ -11,35 +12,59 @@ fn main() {
println!("Lattice CLI v{}", env!("CARGO_PKG_VERSION"));
println!("Type 'help' for commands, 'quit' to exit.\n");
let mut node = match LatticeNodeBuilder::new().build() {
Ok((n, info)) => {
println!("Node ID: {}", info.node_id);
println!("Data: {}", info.data_path);
if info.is_new {
println!("Status: New identity created");
} else if info.entries_replayed > 0 {
println!("Replay: {} log entries applied", info.entries_replayed);
}
println!();
n
}
let (node, info) = match LatticeNodeBuilder::new().build() {
Ok(result) => result,
Err(e) => {
eprintln!("Failed to initialize node: {}", e);
eprintln!("Hint: If data is corrupted, remove the data directory and restart.");
eprintln!("Failed to initialize: {}", e);
return;
}
};
println!("Node ID: {}", info.node_id);
println!("Data: {}", info.data_path);
if info.is_new {
println!("Status: New identity created");
} else if !info.stores.is_empty() {
println!("Stores: {}", info.stores.len());
}
if let Some(root) = info.root_store {
println!("Root: {}", root);
}
let mut current_store: Option<StoreHandle> = match node.open_root_store() {
Ok(Some((h, open_info))) => {
if open_info.entries_replayed > 0 {
println!("Root: {} (replayed {})", open_info.store_id, open_info.entries_replayed);
} else {
println!("Root: {}", open_info.store_id);
}
Some(h)
}
Ok(None) => {
println!("Status: Not initialized (use 'init')");
None
}
Err(e) => {
eprintln!("Warning: {}", e);
None
}
};
println!();
let mut rl = DefaultEditor::new().expect("Failed to create editor");
let cmds = commands::commands();
loop {
match rl.readline("lattice> ") {
let prompt = match &current_store {
Some(h) => format!("lattice:{}> ", &h.id().to_string()[..8]),
None => "lattice:no-store> ".to_string(),
};
match rl.readline(&prompt) {
Ok(line) => {
let line = line.trim();
if line.is_empty() {
continue;
}
if line.is_empty() { continue; }
let _ = rl.add_history_entry(line);
let args = match shlex::split(line) {
@@ -50,40 +75,34 @@ fn main() {
}
};
let cmd_name = match args.first() {
Some(c) => c.as_str(),
None => continue,
};
// Handle quit specially
let cmd_name = args.first().map(|s| s.as_str()).unwrap_or("");
if cmd_name == "quit" || cmd_name == "exit" {
println!("Goodbye!");
break;
}
// Look up command in registry
match cmds.iter().find(|c| c.name == cmd_name) {
Some(cmd) => {
let cmd_args = &args[1..];
if cmd_args.len() < cmd.min_args || cmd_args.len() > cmd.max_args {
if cmd.min_args == cmd.max_args {
println!("Usage: {} {}", cmd.name, cmd.args);
} else {
println!("Usage: {} {} (got {} args)", cmd.name, cmd.args, cmd_args.len());
}
println!("Usage: {} {}", cmd.name, cmd.args);
} else {
(cmd.handler)(&mut node, cmd_args);
match (cmd.handler)(&node, current_store.as_ref(), cmd_args) {
CommandResult::Ok => {}
CommandResult::SwitchTo(h) => current_store = Some(h),
}
}
}
None => println!("Unknown command: '{}'. Type 'help' for commands.", cmd_name),
None => println!("Unknown: '{}'. Type 'help'.", cmd_name),
}
}
Err(ReadlineError::Interrupted | ReadlineError::Eof) => {
println!("Goodbye!");
break;
}
Err(err) => {
eprintln!("Error: {:?}", err);
Err(e) => {
eprintln!("Error: {:?}", e);
break;
}
}