diff --git a/src/lk.rs b/src/lk.rs index 9ca5e44..cbf40c3 100644 --- a/src/lk.rs +++ b/src/lk.rs @@ -26,19 +26,24 @@ impl LK { let comment = name.borrow().comment.clone(); match comment { Some(comment) => { - let mut changed = false; - let new = RE.replace(comment.as_str(), |c: &Captures| { - let folder = c[1].to_string(); - match self.db.get(&folder) { - Some(entry) => { - name.borrow_mut().parent = Some(entry.clone()); - changed = true; + let mut changed = false; + let new = RE + .replace(comment.as_str(), |c: &Captures| { + let folder = c[1].to_string(); + match self.db.get(&folder) { + Some(entry) => { + name.borrow_mut().parent = Some(entry.clone()); + changed = true; + } + None => (), } - None => (), - } - "" - }).to_string(); - if changed && new != comment { name.borrow_mut().comment = if new.len() > 0 { Some(new) } else { None } } + "" + }) + .trim() + .to_string(); + if changed && new != comment { + name.borrow_mut().comment = if new.len() > 0 { Some(new) } else { None } + } } None => (), } diff --git a/src/main.rs b/src/main.rs index 7270014..a1c661e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -9,43 +9,10 @@ mod skey; mod structs; mod utils; -use rpassword::prompt_password; -use rustyline::Editor; -use std::{cell::RefCell, rc::Rc}; - -use crate::lk::LK; -use crate::parser::command_parser; -use crate::repl::{LKEval, LKRead}; -use crate::structs::{Command, LKErr, INIT_FILE}; +use crate::structs::init; pub fn main() { - let lk = Rc::new(RefCell::new(LK::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 { - LKEval::new(cmd, lk.clone(), prompt_password).eval().print(); - } - } - Err(err) => { - LKEval::new(Command::Error(LKErr::ParseError(err)), lk.clone(), prompt_password).eval().print(); - } - }, - Err(err) if err.kind() == std::io::ErrorKind::NotFound => (), - Err(err) => { - LKEval::new( - Command::Error(LKErr::Error( - format!("Failed to read init file {:?}: {}", INIT_FILE.to_str(), err).as_str(), - )), - lk.clone(), - prompt_password, - ) - .eval() - .print(); - } - } - let mut lkread = LKRead::new(Editor::<()>::new().unwrap(), String::from("❯ "), lk.clone()); + let mut lkread = init(); while lkread.read().eval().print() { lkread.refresh(); diff --git a/src/repl.rs b/src/repl.rs index 295770f..86db3a4 100644 --- a/src/repl.rs +++ b/src/repl.rs @@ -17,25 +17,25 @@ use crate::utils::{call_cmd_with_input, get_cmd_args_from_command, get_copy_comm #[derive(Debug)] pub struct LKRead { - rl: Editor<()>, - prompt: String, - state: Rc>, - cmd: String, - read_password: fn(String) -> std::io::Result, + pub rl: Editor<()>, + pub prompt: String, + pub state: Rc>, + pub cmd: String, + pub read_password: fn(String) -> std::io::Result, } #[derive(Debug)] pub struct LKEval<'a> { - cmd: Command<'a>, - state: Rc>, - read_password: fn(String) -> std::io::Result, + pub cmd: Command<'a>, + pub state: Rc>, + pub read_password: fn(String) -> std::io::Result, } #[derive(Debug, PartialEq)] pub struct LKPrint { - out: LKOut, - quit: bool, - state: Rc>, + pub out: LKOut, + pub quit: bool, + pub state: Rc>, } impl LKRead { @@ -129,8 +129,8 @@ impl<'a> LKEval<'a> { match (self.read_password)("Master: ".to_string()) { Ok(password) => { let name = "/".to_string(); - self.state.borrow_mut().secrets.insert(name.to_string(), password.clone()); self.cmd_correct(&out, &name, true, Some(password.clone())); + self.state.borrow_mut().secrets.insert(name, password.clone()); Some(password) } Err(_) => None, @@ -147,14 +147,16 @@ impl<'a> LKEval<'a> { }; if password.is_some() && password.as_ref().unwrap().len() > 0 { let name = pn.borrow().name.to_string(); - self.state.borrow_mut().secrets.insert(name.to_string(), password.as_ref().unwrap().clone()); self.cmd_correct(&out, &name, true, Some(password.as_ref().unwrap().clone())); + self.state.borrow_mut().secrets.insert(name, password.as_ref().unwrap().clone()); password } else { match self.read_master(&out, pn.clone(), read) { Some(master) => { let password = pn.borrow().encode(master.as_str()); - self.state.borrow_mut().secrets.insert(pn.borrow().name.to_string(), password.clone()); + let name = pn.borrow().name.to_string(); + self.cmd_correct(&out, &name, true, Some(master)); + self.state.borrow_mut().secrets.insert(name, password.clone()); Some(password) } None => None, @@ -270,7 +272,9 @@ impl<'a> LKEval<'a> { fn save_dump(data: &HashMap, script: &String) -> std::io::Result<()> { let file = fs::File::create(script)?; let mut writer = BufWriter::new(file); - for (_, pwd) in data { + let mut vals = data.values().map(|v| v.clone()).collect::>(); + vals.sort_by(|a, b| a.borrow().name.cmp(&b.borrow().name)); + for pwd in vals { writeln!(writer, "add {}", pwd.borrow().to_string())? } Ok(()) @@ -439,7 +443,9 @@ impl<'a> LKEval<'a> { fix = true; } } - if fix { state.fix_hierarchy(); } + if fix { + state.fix_hierarchy(); + } } Command::Comment(name, comment) => match self.get_password(name) { Some(pwd) => { @@ -701,7 +707,9 @@ mod tests { vec!["san bud most noon jaw cash".to_string()], vec![ "warning: password / is not marked as correct".to_string(), - "warning: password t3 is not marked as correct".to_string() + "warning: password t1 is not marked as correct".to_string(), + "warning: password t2 is not marked as correct".to_string(), + "warning: password t3 is not marked as correct".to_string(), ] ), false, diff --git a/src/structs.rs b/src/structs.rs index bd975b8..76340ce 100644 --- a/src/structs.rs +++ b/src/structs.rs @@ -1,33 +1,45 @@ use crate::password::{Comment, Name, PasswordRef}; use home::home_dir; -use std::cell::RefCell; +use rpassword::prompt_password; +use rustyline::Editor; use std::fmt; +use std::io::{BufWriter, Write}; use std::path::Path; -use std::rc::Rc; +use std::{cell::RefCell, rc::Rc}; + +use crate::lk::LK; +use crate::parser::command_parser; +use crate::repl::{LKEval, LKRead}; lazy_static! { pub static ref HISTORY_FILE: Box = { - match std::env::var("LESSKEY_HISTORY") { + match std::env::var("HEL_HISTORY") { Ok(v) => Path::new(shellexpand::full(&v).unwrap().into_owned().as_str()).to_path_buf().into_boxed_path(), - _ => home_dir().unwrap().join(".lesskey_history").into_boxed_path(), + _ => home_dir().unwrap().join(".hel_history").into_boxed_path(), + } + }; + pub static ref PROMPT_SETTING: String = { + match std::env::var("HEL_PROMPT") { + Ok(v) => v, + _ => "> ".to_string(), } }; pub static ref INIT_FILE: Box = { - match std::env::var("LESSKEY_INIT") { + match std::env::var("HEL_INIT") { Ok(v) => Path::new(shellexpand::full(&v).unwrap().into_owned().as_str()).to_path_buf().into_boxed_path(), - _ => home_dir().unwrap().join(".lesskeyrc").into_boxed_path(), + _ => home_dir().unwrap().join(".helrc").into_boxed_path(), } }; pub static ref CORRECT_FILE: Box = { - match std::env::var("LESSKEY_CORRECT") { + match std::env::var("HEL_CORRECT") { Ok(v) => Path::new(shellexpand::full(&v).unwrap().into_owned().as_str()).to_path_buf().into_boxed_path(), - _ => home_dir().unwrap().join(".lesskey_correct").into_boxed_path(), + _ => home_dir().unwrap().join(".hel_correct").into_boxed_path(), } }; pub static ref DUMP_FILE: Box = { - match std::env::var("LESSKEY_DUMP") { + match std::env::var("HEL_DUMP") { Ok(v) => Path::new(shellexpand::full(&v).unwrap().into_owned().as_str()).to_path_buf().into_boxed_path(), - _ => home_dir().unwrap().join(".lesskey_dump").into_boxed_path(), + _ => home_dir().unwrap().join(".hel_dump").into_boxed_path(), } }; } @@ -247,20 +259,132 @@ impl fmt::Display for Radix { } } -/* -impl fmt::Display for Radix { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let sign = if self.x < 0 { '-' } else { ' ' }; - let mut x: u32 = self.x.abs() as u32; - write!(f, "{}{}", sign, (0..).map(|_| { - let m = x % self.radix; - x /= self.radix; - (x, std::char::from_digit(m, self.radix).unwrap()) - }) - .take_while(|a| a.0 > 0).map(|b| b.1).collect::() - .chars().rev().collect::() - )?; - Ok(()) +pub fn init() -> LKRead { + let lk = Rc::new(RefCell::new(LK::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 { + LKEval::new(cmd, lk.clone(), prompt_password).eval().print(); + } + } + Err(err) => { + LKEval::new(Command::Error(LKErr::ParseError(err)), lk.clone(), prompt_password).eval().print(); + } + }, + Err(err) if err.kind() == std::io::ErrorKind::NotFound => (), + Err(err) => { + LKEval::new( + Command::Error(LKErr::Error( + format!("Failed to read init file {:?}: {}", INIT_FILE.to_str(), err).as_str(), + )), + lk.clone(), + prompt_password, + ) + .eval() + .print(); + } + } + LKRead::new(Editor::<()>::new().unwrap(), PROMPT_SETTING.to_string(), lk.clone()) +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::password::Password; + use chrono::naive::NaiveDate; + + #[test] + fn test_env() { + std::env::set_var("HEL_HISTORY", "./test_history"); + std::env::set_var("HEL_INIT", "./test_init"); + std::env::set_var("HEL_DUMP", "./test_dump"); + std::env::set_var("HEL_CORRECT", "./test_correct"); + std::env::set_var("HEL_PB", "./test_pb"); + std::env::set_var("HEL_PROMPT", "test> "); + + fn create_init() { + let file = std::fs::File::create("test_init").unwrap(); + let mut writer = BufWriter::new(file); + writeln!(writer, "add t1 r 99 2022-10-10").expect("write"); + writeln!(writer, "add t2 r 99 2022-10-10 test ^t1").expect("write"); + writeln!(writer, "add t3 r 99 2022-10-10 ^t2 aoeu").expect("write"); + } + + #[allow(unused_must_use)] + { + std::fs::remove_file("test_history"); + std::fs::remove_file("test_init"); + std::fs::remove_file("test_dump"); + std::fs::remove_file("test_correct"); + std::fs::remove_file("test_pb"); + } + + create_init(); + let lkread = init(); + assert_eq!(lkread.prompt, "test> "); + assert_eq!(lkread.state.borrow().db.contains_key("t1"), true); + + let t1 = Rc::new(RefCell::new(Password::new( + None, + "t1".to_string(), + None, + Mode::Regular, + 99, + NaiveDate::from_ymd_opt(2022, 10, 10).unwrap(), + None, + ))); + let t2 = Rc::new(RefCell::new(Password::new( + None, + "t2".to_string(), + None, + Mode::Regular, + 99, + NaiveDate::from_ymd_opt(2022, 10, 10).unwrap(), + Some("test".to_string()), + ))); + t2.borrow_mut().parent = Some(t1.clone()); + let t3 = Rc::new(RefCell::new(Password::new( + None, + "t3".to_string(), + None, + Mode::Regular, + 99, + NaiveDate::from_ymd_opt(2022, 10, 10).unwrap(), + Some("aoeu".to_string()), + ))); + t3.borrow_mut().parent = Some(t2.clone()); + assert_eq!(*lkread.state.borrow().db.get("t1").unwrap().borrow(), *t1.borrow()); + assert_eq!(*lkread.state.borrow().db.get("t2").unwrap().borrow(), *t2.borrow()); + assert_eq!(*lkread.state.borrow().db.get("t3").unwrap().borrow(), *t3.borrow()); + + LKEval::new(command_parser::cmd("dump").unwrap(), lkread.state.clone(), prompt_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 t2").unwrap(), lkread.state.clone(), |v| { + if v == "/" { + Ok("a".to_string()) + } else { + Ok("".to_string()) + } + }) + .eval(); + assert_eq!( + pr.out, + LKOut::from_vecs( + vec!["mid date os gaur gear let".to_string()], + vec![ + "warning: password / is not marked as correct".to_string(), + "warning: password t1 is not marked as correct".to_string(), + "warning: password t2 is not marked as correct".to_string(), + ] + ) + ); } } -*/ diff --git a/src/utils.rs b/src/utils.rs index 70f1a1f..3b33535 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -37,7 +37,7 @@ pub fn get_cmd_args_from_command(command: &str) -> io::Result<(String, Vec (String, Vec) { - let cmd_os_str = env::var_os("LESSKEY_PB").unwrap_or_else(|| match env::consts::OS { + let cmd_os_str = env::var_os("HEL_PB").unwrap_or_else(|| match env::consts::OS { _ if env::var("TMUX").is_ok() => OsString::from("tmux load-buffer -"), "macos" => OsString::from("pbcopy"), "linux" => OsString::from("xclip"), diff --git a/test_dump b/test_dump new file mode 100644 index 0000000..3ec91dc --- /dev/null +++ b/test_dump @@ -0,0 +1,3 @@ +add t1 R 99 2022-10-10 +add t2 R 99 2022-10-10 test ^t1 +add t3 R 99 2022-10-10 aoeu ^t2 diff --git a/test_init b/test_init new file mode 100644 index 0000000..e3de464 --- /dev/null +++ b/test_init @@ -0,0 +1,3 @@ +add t1 r 99 2022-10-10 +add t2 r 99 2022-10-10 test ^t1 +add t3 r 99 2022-10-10 ^t2 aoeu