diff --git a/hel/src/commands.rs b/hel/src/commands.rs index 30cf67e..0c11afb 100644 --- a/hel/src/commands.rs +++ b/hel/src/commands.rs @@ -206,7 +206,7 @@ impl<'a> LKEval<'a> { pub fn cmd_pb(&self, out: &LKOut, command: &String) { match command_parser::cmd(command) { Ok(cmd) => { - let print = LKEval::new(cmd, self.state.clone(), self.read_password).eval(); + let print = LKEval::new(self.rl.clone(), cmd, self.state.clone(), self.read_password).eval(); let data = print.out.data(); print.out.copy_err(&out); if data.len() > 0 { @@ -257,7 +257,7 @@ impl<'a> LKEval<'a> { match command_parser::script(&script) { Ok(cmd_list) => { for cmd in cmd_list { - let print = LKEval::new(cmd, self.state.clone(), password).eval(); + let print = LKEval::new(self.rl.clone(), cmd, self.state.clone(), password).eval(); print.out.copy(&out); if print.quit { return true; diff --git a/hel/src/repl.rs b/hel/src/repl.rs index a82f41e..588fe62 100644 --- a/hel/src/repl.rs +++ b/hel/src/repl.rs @@ -1,11 +1,11 @@ use crate::lk::LKRef; use crate::parser::command_parser; use crate::structs::{Command, LKErr, LKOut, HISTORY_FILE}; -use crate::utils::editor::{password, Editor}; +use crate::utils::editor::{password, Editor, EditorRef}; #[derive(Debug)] pub struct LKRead { - pub rl: Editor, + pub rl: EditorRef, pub prompt: String, pub state: LKRef, pub cmd: String, @@ -15,6 +15,7 @@ pub struct LKRead { #[derive(Debug)] pub struct LKEval<'a> { + pub rl: EditorRef, pub cmd: Command<'a>, pub state: LKRef, pub read_password: fn(String) -> std::io::Result, @@ -28,7 +29,7 @@ pub struct LKPrint { } impl LKRead { - pub fn new(rl: Editor, prompt: String, state: LKRef) -> Self { + pub fn new(rl: EditorRef, prompt: String, state: LKRef) -> Self { Self { rl, prompt, @@ -40,22 +41,14 @@ impl LKRead { } pub fn read(&mut self) -> LKEval { - let history_file = HISTORY_FILE.to_str().unwrap(); - self.rl.clear_history(); - match self.rl.load_history(&history_file) { - Ok(_) => (), - Err(_) => { - self.rl.add_history_entry("ls"); - () - } - } self.cmd = match &self.input { Some(cmd) => cmd.to_string(), - None => match self.rl.readline(&*self.prompt) { + None => match self.rl.lock().readline(&*self.prompt) { Ok(str) => str, Err(LKErr::EOF) => "quit".to_string(), Err(err) => { return LKEval::new( + self.rl.clone(), Command::Error(LKErr::ReadError(err.to_string())), self.state.clone(), self.read_password, @@ -63,14 +56,9 @@ impl LKRead { } } }; - self.rl.add_history_entry(self.cmd.as_str()); - match self.rl.save_history(&history_file) { - Ok(_) => (), - Err(_) => (), - } match command_parser::cmd(&self.cmd) { - Ok(cmd) => LKEval::new(cmd, self.state.clone(), self.read_password), - Err(err) => LKEval::new(Command::Error(LKErr::ParseError(err)), self.state.clone(), self.read_password), + Ok(cmd) => LKEval::new(self.rl.clone(), cmd, self.state.clone(), self.read_password), + Err(err) => LKEval::new(self.rl.clone(), Command::Error(LKErr::ParseError(err)), self.state.clone(), self.read_password), } } @@ -80,17 +68,31 @@ impl LKRead { } impl<'a> LKEval<'a> { - pub fn new(cmd: Command<'a>, state: LKRef, read_password: fn(String) -> std::io::Result) -> Self { + pub fn new(rl: EditorRef, cmd: Command<'a>, state: LKRef, read_password: fn(String) -> std::io::Result) -> Self { Self { + rl, cmd, state, read_password, } } + pub fn news(cmd: Command<'a>, state: LKRef) -> Self { + LKEval::new(Editor::new(), cmd, state, |_| { Err(std::io::Error::new(std::io::ErrorKind::NotConnected, "could not read password")) }) + } + + pub fn newd(cmd: Command<'a>, state: LKRef, read_password: fn(String) -> std::io::Result) -> Self { + LKEval::new(Editor::new(), cmd, state, read_password) + } + pub fn eval(&self) -> LKPrint { let out = LKOut::new(); let mut quit: bool = false; + let history_file = HISTORY_FILE.to_str().unwrap(); + let mut to_history = true; + + self.rl.lock().clear_history(); + self.rl.lock().load_history(&history_file).ok(); match &self.cmd { Command::Quit => { @@ -122,26 +124,34 @@ impl<'a> LKEval<'a> { quit = self.cmd_source(&out, script); } Command::Dump(script) => self.cmd_dump(&out, script), - Command::Pass(name, pass) => self.cmd_pass(&out, &name, &pass), + Command::Pass(name, pass) => { to_history = false; self.cmd_pass(&out, &name, &pass); }, Command::UnPass(name) => match self.state.lock().borrow_mut().secrets.remove(name) { Some(_) => out.o(format!("Removed saved password for {}", name)), None => out.e(format!("error: saved password for {} not found", name)), }, Command::Correct(name) => self.cmd_correct(&out, name, true, None), Command::Uncorrect(name) => self.cmd_correct(&out, name, false, None), - Command::Noop => (), + Command::Noop => { to_history = false; }, Command::Help => { out.o("HELP".to_string()); } Command::Mv(name, folder) => self.cmd_mv(&out, &name, &folder), - Command::Error(error) => match error { - LKErr::ParseError(e) => out.e(e.to_string()), - LKErr::ReadError(e) => out.e(e.to_string()), - LKErr::EOF => out.e("error: end of file".to_string()), - LKErr::Error(e) => out.e(format!("error: {}", e.to_string())), + Command::Error(error) => { + to_history = false; + match error { + LKErr::ParseError(e) => out.e(e.to_string()), + LKErr::ReadError(e) => out.e(e.to_string()), + LKErr::EOF => out.e("error: end of file".to_string()), + LKErr::Error(e) => out.e(format!("error: {}", e.to_string())), + }; }, } + if to_history { + self.rl.lock().add_history_entry(self.cmd.to_string().as_str()); + self.rl.lock().save_history(&history_file).ok(); + } + LKPrint::new(out, quit, self.state.clone()) } } @@ -177,18 +187,6 @@ mod tests { use std::collections::HashMap; use std::sync::Arc; - impl<'a> LKEval<'a> { - pub fn news(cmd: Command<'a>, state: LKRef) -> Self { - Self { - cmd, - state, - read_password: |_| { - Err(std::io::Error::new(std::io::ErrorKind::NotConnected, "could not read password")) - }, - } - } - } - #[test] fn exec_cmds_basic() { let lk = Arc::new(ReentrantMutex::new(RefCell::new(LK::new()))); @@ -346,7 +344,7 @@ mod tests { LKPrint::new(LKOut::from_vecs(vec![], vec![]), false, lk.clone()) ); assert_eq!( - LKEval::new(Command::Enc("t3".to_string()), lk.clone(), |p| if p == "NULL" { + LKEval::newd(Command::Enc("t3".to_string()), lk.clone(), |p| if p == "NULL" { Ok("a".to_string()) } else { Err(std::io::Error::new(std::io::ErrorKind::NotFound, "test")) @@ -359,7 +357,7 @@ mod tests { ) ); assert_eq!( - LKEval::new(Command::Enc("t3".to_string()), lk.clone(), |p| if p == "Master: " { + LKEval::newd(Command::Enc("t3".to_string()), lk.clone(), |p| if p == "Master: " { Ok("a".to_string()) } else { Err(std::io::Error::new(std::io::ErrorKind::NotFound, "test")) @@ -380,7 +378,7 @@ mod tests { ) ); assert_eq!( - LKEval::new(Command::Enc("t2".to_string()), lk.clone(), |p| if p == "NULL" { + LKEval::newd(Command::Enc("t2".to_string()), lk.clone(), |p| if p == "NULL" { Ok("a".to_string()) } else { Err(std::io::Error::new(std::io::ErrorKind::NotFound, "test")) @@ -396,7 +394,7 @@ mod tests { ) ); assert_eq!( - LKEval::new(Command::Enc("t1".to_string()), lk.clone(), |p| if p == "NULL" { + LKEval::newd(Command::Enc("t1".to_string()), lk.clone(), |p| if p == "NULL" { Ok("a".to_string()) } else { Err(std::io::Error::new(std::io::ErrorKind::NotFound, "test")) @@ -426,7 +424,7 @@ mod tests { None, )); LKEval::news(Command::Add(t1.clone()), lk.clone()).eval(); - ({ let mut e = LKEval::news(Command::Pass("t1".to_string(), None), lk.clone()); e.read_password = |_| { Ok("test pwd1".to_string()) }; e }).eval(); + LKEval::newd(Command::Pass("t1".to_string(), None), lk.clone(), |_| { Ok("test pwd1".to_string()) }).eval(); assert_eq!(lk.lock().borrow().secrets[&"t1".to_string()], "test pwd1"); LKEval::news(Command::Pass("t1".to_string(), Some("other pw".to_string())), lk.clone()).eval(); assert_eq!(lk.lock().borrow().secrets[&"t1".to_string()], "other pw"); diff --git a/hel/src/structs.rs b/hel/src/structs.rs index d7dacb9..c3b770c 100644 --- a/hel/src/structs.rs +++ b/hel/src/structs.rs @@ -109,6 +109,36 @@ impl<'a> PartialEq for Command<'a> { } } +impl<'a> std::fmt::Display for Command<'a> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Command::Add(s) => write!(f, "add {}", s.lock().borrow().to_string()), + Command::Keep(s) => write!(f, "keep {}", s), + Command::Ls(s) => write!(f, "ls {}", s), + Command::Ld(s) => write!(f, "ld {}", s), + Command::Mv(a, b) => write!(f, "mv {} {}", a, b), + Command::Rm(s) => write!(f, "rm {}", s), + Command::Enc(s) => write!(f, "enc {}", s), + Command::Gen(a, b) => write!(f, "gen{} {}", a, b.lock().borrow().to_string()), + Command::Pass(a, None) => write!(f, "pass {}", a), + Command::Pass(a, Some(b)) => write!(f, "pass {} {}", a, b), + Command::UnPass(s) => write!(f, "unpass {}", s), + Command::Correct(s) => write!(f, "correct {}", s), + Command::Uncorrect(s) => write!(f, "uncorrect {}", s), + Command::PasteBuffer(s) => write!(f, "pb {}", s), + Command::Source(s) => write!(f, "source {}", s), + Command::Dump(None) => write!(f, "dump"), + Command::Dump(Some(s)) => write!(f, "dump {}", s), + Command::Comment(a, None) => write!(f, "comment {}", a), + Command::Comment(a, Some(b)) => write!(f, "comment {} {}", a, b), + Command::Error(s) => write!(f, "error {}", s), + Command::Noop => write!(f, "noop"), + Command::Help => write!(f, "help"), + Command::Quit => write!(f, "quit"), + } + } +} + #[derive(PartialEq, Debug, Clone)] pub enum Mode { Regular, @@ -322,23 +352,25 @@ impl fmt::Display for Radix { pub fn init() -> Option { let lk = Arc::new(ReentrantMutex::new(RefCell::new(LK::new()))); + let editor = Editor::new(); match std::fs::read_to_string(INIT_FILE.to_str().unwrap()) { Ok(script) => match command_parser::script(&script) { Ok(cmd_list) => { for cmd in cmd_list { - if !LKEval::new(cmd, lk.clone(), password).eval().print() { + if !LKEval::new(editor.clone(), cmd, lk.clone(), password).eval().print() { return None; } } } Err(err) => { - LKEval::new(Command::Error(LKErr::ParseError(err)), lk.clone(), password).eval().print(); + LKEval::new(editor.clone(), Command::Error(LKErr::ParseError(err)), lk.clone(), password).eval().print(); } }, Err(err) if err.kind() == std::io::ErrorKind::NotFound => (), Err(err) => { LKEval::new( + editor.clone(), Command::Error(LKErr::Error( format!("Failed to read init file {:?}: {}", INIT_FILE.to_str(), err).as_str(), )), @@ -349,7 +381,7 @@ pub fn init() -> Option { .print(); } } - Some(LKRead::new(Editor::new(), PROMPT_SETTING.to_string(), lk.clone())) + Some(LKRead::new(editor.clone(), PROMPT_SETTING.to_string(), lk.clone())) } #[cfg(test)] @@ -444,13 +476,13 @@ mod tests { assert_eq!(*lkread.state.lock().borrow().db.get("t2").unwrap().lock(), *t2.lock()); assert_eq!(*lkread.state.lock().borrow().db.get("t3").unwrap().lock(), *t3.lock()); - LKEval::new(command_parser::cmd("save").unwrap(), lkread.state.clone(), password).eval().print(); + LKEval::newd(command_parser::cmd("save").unwrap(), lkread.state.clone(), password).eval().print(); assert_eq!( std::fs::read_to_string("test_dump").expect("read"), "add t1 R 99 2022-10-10\nadd t2 R 99 2022-10-10 test ^t1\nadd t3 R 99 2022-10-10 aoeu ^t2\n".to_string() ); - let pr = LKEval::new(command_parser::cmd("enc t3").unwrap(), lkread.state.clone(), |v| { + let pr = LKEval::newd(command_parser::cmd("enc t3").unwrap(), lkread.state.clone(), |v| { if v == "/" { Ok("a".to_string()) } else { @@ -471,7 +503,7 @@ mod tests { ) ); lkread.state.lock().borrow_mut().secrets.clear(); - let pr = LKEval::new(command_parser::cmd("pb enc t3").unwrap(), lkread.state.clone(), |v| { + let pr = LKEval::newd(command_parser::cmd("pb enc t3").unwrap(), lkread.state.clone(), |v| { if v == "/" { Ok("a".to_string()) } else { diff --git a/hel/src/utils.rs b/hel/src/utils.rs index 6a32349..555b8f7 100644 --- a/hel/src/utils.rs +++ b/hel/src/utils.rs @@ -91,6 +91,10 @@ pub mod home { pub mod editor { use crate::structs::LKErr; use rustyline::error::ReadlineError; + use std::sync::Arc; + use parking_lot::Mutex; + + pub type EditorRef = Arc>; #[derive(Debug)] pub struct Editor { @@ -98,10 +102,10 @@ pub mod editor { } impl Editor { - pub fn new() -> Self { - Self { + pub fn new() -> EditorRef { + Arc::new(Mutex::new(Self { editor: rustyline::Editor::<()>::new().unwrap(), - } + })) } pub fn clear_history(&mut self) {