diff options
author | Alex Auvolat <alex@adnab.me> | 2021-11-03 23:26:37 +0100 |
---|---|---|
committer | Alex Auvolat <alex@adnab.me> | 2021-11-03 23:26:37 +0100 |
commit | 5160a7637b60dc45cc93836b4749a8b929b183de (patch) | |
tree | 0b1613d359334cae5f8de40b295b50331dd69892 /src/main.rs | |
download | sledcli-5160a7637b60dc45cc93836b4749a8b929b183de.tar.gz sledcli-5160a7637b60dc45cc93836b4749a8b929b183de.zip |
First commit
Diffstat (limited to 'src/main.rs')
-rw-r--r-- | src/main.rs | 162 |
1 files changed, 162 insertions, 0 deletions
diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..a2de0e1 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,162 @@ +use std::path::PathBuf; +use std::ops::Deref; + +use anyhow::Result; + +use rustyline::error::ReadlineError; +use rustyline::Editor; + +use structopt::StructOpt; + +use sled::IVec; + +#[derive(StructOpt, Debug)] +#[structopt(name = "sledcli")] +struct Opt { + /// Path to Sled database + #[structopt(name="path")] + path: PathBuf, +} + +enum DisplayMode { + TryString, + HexDump, + Mixed, +} + +struct State { + db: sled::Db, + tree: sled::Tree, + displaymode: DisplayMode, +} + +fn main() { + let opt = Opt::from_args(); + let db = sled::Config::default() + .path(&opt.path) + .open() + .expect("Unable to open database"); + + let tree: sled::Tree = db.deref().clone(); + let mut state = State { + db, + tree, + displaymode: DisplayMode::Mixed, + }; + + let mut readline = Editor::<()>::new(); + loop { + let lineread = readline.readline(&format!("{}> ", try_string(&state.tree.name()))); + match lineread { + Ok(line) => { + readline.add_history_entry(line.as_str()); + if let Err(e) = do_command(&line, &mut state) { + println!("Error: {}", e); + } + }, + Err(ReadlineError::Interrupted) => { + println!("^C"); + continue; + }, + Err(ReadlineError::Eof) => { + break + }, + Err(err) => { + println!("Readline error: {:?}", err); + break + } + } + } +} + +fn try_string(input: &sled::IVec) -> String { + let mut string = String::new(); + utf8::LossyDecoder::new(|s| string.push_str(s)).feed(input); + string +} + +fn do_command(line: &str, state: &mut State) -> Result<()> { + let parts = line.split(' ').filter(|part| part.len() > 0).collect::<Vec<_>>(); + if parts.is_empty() { + return Ok(()); + } + + match &parts[..] { + ["ls"] => { + let mut names = state.db.tree_names(); + names.sort(); + for name in names { + println!("{}", try_string(&name)); + } + } + ["cd", treename] => { + if state.db.tree_names().iter().any(|t| t == treename.as_bytes()) { + state.tree = state.db.open_tree(treename.as_bytes())?; + } else { + println!("Tree {} does not exist", treename); + } + } + ["hex"] => { + state.displaymode = DisplayMode::HexDump; + } + ["str"] => { + state.displaymode = DisplayMode::TryString; + } + ["mix"] => { + state.displaymode = DisplayMode::Mixed; + } + ["keys"] => { + for (i, pair) in state.tree.iter().enumerate() { + if i >= 20 { + println!("..."); + break; + } + let (k, _v) = pair?; + state.displaymode.print_key(&k); + } + } + ["pairs"] => { + for (i, pair) in state.tree.iter().enumerate() { + if i >= 20 { + println!("..."); + break; + } + let (k, v) = pair?; + state.displaymode.print_pair(&k, &v); + } + } + bad_cmd => println!("Unrecognized command: {:?}", bad_cmd), + } + Ok(()) +} + +impl DisplayMode { + fn print_key(&self, k: &IVec) { + match *self { + DisplayMode::HexDump => { + hexdump::hexdump(k); + } + _ => { + println!("{}", try_string(k)); + } + } + } + + fn print_pair(&self, k: &IVec, v: &IVec) { + match *self { + DisplayMode::HexDump => { + hexdump::hexdump(k); + hexdump::hexdump(v); + println!(); + } + DisplayMode::Mixed => { + println!("{}", try_string(k)); + hexdump::hexdump(v); + println!(); + } + DisplayMode::TryString => { + println!("{}\t{}", try_string(k), try_string(v)); + } + } + } +} |