aboutsummaryrefslogblamecommitdiff
path: root/src/main.rs
blob: cca2c4399e04d181bf72ab27cf9ba0f643e0f3a1 (plain) (tree)
1
2
3
4
5
6
7
                    
                       


                   

                                          










                                    
                                   





























                                                   










                                                                                          


                                                                               

                                                            

                                               







                                                                      


                                             
                                       




                                                                    



                                              











                                                                  


                                                              
                                          

                                                                            
                                              

                                                                             
                                                      



                                                              

                                               







                                                                      

                                                   






                                                                                              


                                                                                                      
                 
                                     





                                                                  




































































                                                                                      
use std::ops::Deref;
use std::path::PathBuf;

use anyhow::Result;

use humansize::{file_size_opts, FileSize};

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 prefix = match state.displaymode {
			DisplayMode::HexDump => "hex",
			DisplayMode::TryString => "str",
			DisplayMode::Mixed => "mix",
		};
		let prompt = format!("[{}] {}> ", prefix, try_string(&state.tree.name()));
		let lineread = readline.readline(&prompt);
		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));
			}
		}
		["ll"] => {
			let mut names = state.db.tree_names();
			names.sort();
			let mut total = 0;
			for name in names {
				let nent = state.db.open_tree(&name)?.len();
				total += nent;
				println!("{:8} {}", nent, try_string(&name));
			}
			println!("{:8} TOTAL", total);
		}
		["lu"] => {
			let mut names = state.db.tree_names();
			names.sort();
			let mut total_nent = 0;
			let mut total_size = 0;
			for name in names {
				let tree = state.db.open_tree(&name)?;
				let nent = tree.len();
				let mut size = 0;
				for ent in tree.iter() {
					let (k, v) = ent?;
					size += k.len() + v.len();
				}
				total_nent += nent;
				total_size += size;
				println!(
					"{:8} {:>12} {}",
					nent,
					size.file_size(file_size_opts::CONVENTIONAL).unwrap(),
					try_string(&name)
				);
			}
			println!("{:8} {:>12} TOTAL",
					 total_nent,
					 total_size.file_size(file_size_opts::CONVENTIONAL).unwrap());
		}
		["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));
			}
		}
	}
}