diff --git a/Cargo.toml b/Cargo.toml index 01d316c..d907077 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] -name = "my-project" +name = "lesskey" version = "0.1.0" -authors = ["runner"] +authors = ["ok2"] edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/src/main.rs b/src/main.rs index 01914d0..4537e5d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,12 +1,17 @@ #[macro_use] extern crate lazy_static; -pub mod structs; -pub mod parser; -pub mod repl; +mod structs; +mod parser; +mod repl; + +use std::{cell::RefCell, rc::Rc}; +use std::collections::HashMap; use rustyline::Editor; -use std::{cell::RefCell, rc::Rc}; + +use crate::structs::LK; +use crate::repl::LKRead; pub fn main() { let lk = Rc::new(RefCell::new(LK { db: HashMap::new() })); diff --git a/src/parser.rs b/src/parser.rs index f8aeb85..e5adbe0 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -1,7 +1,11 @@ extern crate peg; +use std::{cell::RefCell, rc::Rc}; +use chrono::naive::NaiveDate; +use crate::structs::{Mode, Command, Password, LKErr}; + peg::parser!{ - grammar command_parser() for str { + pub grammar command_parser() for str { pub rule cmd() -> Command<'input> = c:( help_cmd() / add_cmd() @@ -66,60 +70,60 @@ peg::parser!{ #[cfg(test)] mod tests { - use super::*; + use super::*; - #[test] - fn parse_password_test() { - assert_eq!(command_parser::name("ableton89 R 99 2020-12-09 xx.ableton@domain.info https://www.ableton.com"), - Ok(Password { name: "ableton89".to_string(), parent: None, prefix: None, mode: Mode::Regular, - length: None, seq: 99, date: NaiveDate::from_ymd_opt(2020, 12, 09).unwrap(), - comment: Some("xx.ableton@domain.info https://www.ableton.com".to_string()) })); - assert_eq!(command_parser::name("ableton89 U 99 2020-12-09 xx.ableton@domain.info https://www.ableton.com"), - Ok(Password { name: "ableton89".to_string(), parent: None, prefix: None, mode: Mode::RegularUpcase, - length: None, seq: 99, date: NaiveDate::from_ymd_opt(2020, 12, 09).unwrap(), - comment: Some("xx.ableton@domain.info https://www.ableton.com".to_string()) })); - assert_eq!(command_parser::name("ableton89 U 2020-12-09"), - Ok(Password { name: "ableton89".to_string(), parent: None, prefix: None, mode: Mode::RegularUpcase, - length: None, seq: 99, date: NaiveDate::from_ymd_opt(2020, 12, 09).unwrap(), comment: None })); - assert_eq!(command_parser::name("#W9 ableton89 R 99 2020-12-09 xx.ableton@domain.info https://www.ableton.com"), - Ok(Password { name: "ableton89".to_string(), parent: None, prefix: Some("#W9".to_string()), mode: Mode::Regular, - length: None, seq: 99, date: NaiveDate::from_ymd_opt(2020, 12, 09).unwrap(), - comment: Some("xx.ableton@domain.info https://www.ableton.com".to_string()) })); - assert_eq!(command_parser::name("#W9 ableton89 N 99 2020-12-09 xx.ableton@domain.info https://www.ableton.com"), - Ok(Password { name: "ableton89".to_string(), parent: None, prefix: Some("#W9".to_string()), mode: Mode::NoSpace, - length: None, seq: 99, date: NaiveDate::from_ymd_opt(2020, 12, 09).unwrap(), - comment: Some("xx.ableton@domain.info https://www.ableton.com".to_string()) })); - assert_eq!(command_parser::name("#W9 ableton89 UN 99 2020-12-09 xx.ableton@domain.info https://www.ableton.com"), - Ok(Password { name: "ableton89".to_string(), parent: None, prefix: Some("#W9".to_string()), mode: Mode::NoSpaceUpcase, - length: None, seq: 99, date: NaiveDate::from_ymd_opt(2020, 12, 09).unwrap(), - comment: Some("xx.ableton@domain.info https://www.ableton.com".to_string()) })); - assert_eq!(command_parser::name("#W9 ableton89 20R 99 2020-12-09 a b c"), - Ok(Password { name: "ableton89".to_string(), parent: None, prefix: Some("#W9".to_string()), mode: Mode::Regular, - length: Some(20), seq: 99, date: NaiveDate::from_ymd_opt(2020, 12, 09).unwrap(), - comment: Some("a b c".to_string()) })); - assert_eq!(command_parser::name("#W9 ableton89 20UR 99 2020-12-09 a b c"), - Ok(Password { name: "ableton89".to_string(), parent: None, prefix: Some("#W9".to_string()), mode: Mode::RegularUpcase, - length: Some(20), seq: 99, date: NaiveDate::from_ymd_opt(2020, 12, 09).unwrap(), - comment: Some("a b c".to_string()) })); - assert_eq!(command_parser::name("#W9 ableton89 20UH 99 2020-12-09 a b c"), - Ok(Password { name: "ableton89".to_string(), parent: None, prefix: Some("#W9".to_string()), mode: Mode::HexUpcase, - length: Some(20), seq: 99, date: NaiveDate::from_ymd_opt(2020, 12, 09).unwrap(), - comment: Some("a b c".to_string()) })); - assert_eq!(command_parser::name("#W9 ableton89 20UB 99 2020-12-09 a b c"), - Ok(Password { name: "ableton89".to_string(), parent: None, prefix: Some("#W9".to_string()), mode: Mode::Base64Upcase, - length: Some(20), seq: 99, date: NaiveDate::from_ymd_opt(2020, 12, 09).unwrap(), - comment: Some("a b c".to_string()) })); - assert_eq!(command_parser::name("#W9 ableton89 20D 99 2020-12-09 a b c"), - Ok(Password { name: "ableton89".to_string(), parent: None, prefix: Some("#W9".to_string()), mode: Mode::Decimal, - length: Some(20), seq: 99, date: NaiveDate::from_ymd_opt(2020, 12, 09).unwrap(), - comment: Some("a b c".to_string()) })); - assert_eq!(command_parser::name("ableton89 20D 98 2020-12-09 a b c"), - Ok(Password { name: "ableton89".to_string(), parent: None, prefix: None, mode: Mode::Decimal, - length: Some(20), seq: 98, date: NaiveDate::from_ymd_opt(2020, 12, 09).unwrap(), - comment: Some("a b c".to_string()) })); - assert_eq!(command_parser::name("ableton89 20D 2020-12-09 a b c"), - Ok(Password { name: "ableton89".to_string(), parent: None, prefix: None, mode: Mode::Decimal, - length: Some(20), seq: 99, date: NaiveDate::from_ymd_opt(2020, 12, 09).unwrap(), - comment: Some("a b c".to_string()) })); - } + #[test] + fn parse_password_test() { + assert_eq!(command_parser::name("ableton89 R 99 2020-12-09 xx.ableton@domain.info https://www.ableton.com"), + Ok(Password { name: Rc::new("ableton89".to_string()), parent: None, prefix: None, mode: Mode::Regular, + length: None, seq: 99, date: NaiveDate::from_ymd_opt(2020, 12, 09).unwrap(), + comment: Some("xx.ableton@domain.info https://www.ableton.com".to_string()) })); + assert_eq!(command_parser::name("ableton89 U 99 2020-12-09 xx.ableton@domain.info https://www.ableton.com"), + Ok(Password { name: Rc::new("ableton89".to_string()), parent: None, prefix: None, mode: Mode::RegularUpcase, + length: None, seq: 99, date: NaiveDate::from_ymd_opt(2020, 12, 09).unwrap(), + comment: Some("xx.ableton@domain.info https://www.ableton.com".to_string()) })); + assert_eq!(command_parser::name("ableton89 U 2020-12-09"), + Ok(Password { name: Rc::new("ableton89".to_string()), parent: None, prefix: None, mode: Mode::RegularUpcase, + length: None, seq: 99, date: NaiveDate::from_ymd_opt(2020, 12, 09).unwrap(), comment: None })); + assert_eq!(command_parser::name("#W9 ableton89 R 99 2020-12-09 xx.ableton@domain.info https://www.ableton.com"), + Ok(Password { name: Rc::new("ableton89".to_string()), parent: None, prefix: Some("#W9".to_string()), mode: Mode::Regular, + length: None, seq: 99, date: NaiveDate::from_ymd_opt(2020, 12, 09).unwrap(), + comment: Some("xx.ableton@domain.info https://www.ableton.com".to_string()) })); + assert_eq!(command_parser::name("#W9 ableton89 N 99 2020-12-09 xx.ableton@domain.info https://www.ableton.com"), + Ok(Password { name: Rc::new("ableton89".to_string()), parent: None, prefix: Some("#W9".to_string()), mode: Mode::NoSpace, + length: None, seq: 99, date: NaiveDate::from_ymd_opt(2020, 12, 09).unwrap(), + comment: Some("xx.ableton@domain.info https://www.ableton.com".to_string()) })); + assert_eq!(command_parser::name("#W9 ableton89 UN 99 2020-12-09 xx.ableton@domain.info https://www.ableton.com"), + Ok(Password { name: Rc::new("ableton89".to_string()), parent: None, prefix: Some("#W9".to_string()), mode: Mode::NoSpaceUpcase, + length: None, seq: 99, date: NaiveDate::from_ymd_opt(2020, 12, 09).unwrap(), + comment: Some("xx.ableton@domain.info https://www.ableton.com".to_string()) })); + assert_eq!(command_parser::name("#W9 ableton89 20R 99 2020-12-09 a b c"), + Ok(Password { name: Rc::new("ableton89".to_string()), parent: None, prefix: Some("#W9".to_string()), mode: Mode::Regular, + length: Some(20), seq: 99, date: NaiveDate::from_ymd_opt(2020, 12, 09).unwrap(), + comment: Some("a b c".to_string()) })); + assert_eq!(command_parser::name("#W9 ableton89 20UR 99 2020-12-09 a b c"), + Ok(Password { name: Rc::new("ableton89".to_string()), parent: None, prefix: Some("#W9".to_string()), mode: Mode::RegularUpcase, + length: Some(20), seq: 99, date: NaiveDate::from_ymd_opt(2020, 12, 09).unwrap(), + comment: Some("a b c".to_string()) })); + assert_eq!(command_parser::name("#W9 ableton89 20UH 99 2020-12-09 a b c"), + Ok(Password { name: Rc::new("ableton89".to_string()), parent: None, prefix: Some("#W9".to_string()), mode: Mode::HexUpcase, + length: Some(20), seq: 99, date: NaiveDate::from_ymd_opt(2020, 12, 09).unwrap(), + comment: Some("a b c".to_string()) })); + assert_eq!(command_parser::name("#W9 ableton89 20UB 99 2020-12-09 a b c"), + Ok(Password { name: Rc::new("ableton89".to_string()), parent: None, prefix: Some("#W9".to_string()), mode: Mode::Base64Upcase, + length: Some(20), seq: 99, date: NaiveDate::from_ymd_opt(2020, 12, 09).unwrap(), + comment: Some("a b c".to_string()) })); + assert_eq!(command_parser::name("#W9 ableton89 20D 99 2020-12-09 a b c"), + Ok(Password { name: Rc::new("ableton89".to_string()), parent: None, prefix: Some("#W9".to_string()), mode: Mode::Decimal, + length: Some(20), seq: 99, date: NaiveDate::from_ymd_opt(2020, 12, 09).unwrap(), + comment: Some("a b c".to_string()) })); + assert_eq!(command_parser::name("ableton89 20D 98 2020-12-09 a b c"), + Ok(Password { name: Rc::new("ableton89".to_string()), parent: None, prefix: None, mode: Mode::Decimal, + length: Some(20), seq: 98, date: NaiveDate::from_ymd_opt(2020, 12, 09).unwrap(), + comment: Some("a b c".to_string()) })); + assert_eq!(command_parser::name("ableton89 20D 2020-12-09 a b c"), + Ok(Password { name: Rc::new("ableton89".to_string()), parent: None, prefix: None, mode: Mode::Decimal, + length: Some(20), seq: 99, date: NaiveDate::from_ymd_opt(2020, 12, 09).unwrap(), + comment: Some("a b c".to_string()) })); + } } \ No newline at end of file diff --git a/src/repl.rs b/src/repl.rs index 4a4b301..8fdcc20 100644 --- a/src/repl.rs +++ b/src/repl.rs @@ -1,5 +1,11 @@ +use rustyline::Editor; +use std::{cell::RefCell, rc::Rc}; + +use crate::structs::{LKErr, Command, LK}; +use crate::parser::command_parser; + #[derive(Debug)] -struct LKRead { +pub struct LKRead { rl: Editor::<()>, prompt: String, state: Rc>, @@ -7,47 +13,47 @@ struct LKRead { } #[derive(Debug)] -struct LKEval<'a> { +pub struct LKEval<'a> { cmd: Command<'a>, state: Rc>, } -#[derive(Debug)] -struct LKPrint { +#[derive(Debug, PartialEq)] +pub struct LKPrint { out: Vec, quit: bool, - // state: Rc>, + state: Rc>, } impl LKRead { - fn new(rl: Editor::<()>, prompt: String, state: Rc>) -> Self { + pub fn new(rl: Editor::<()>, prompt: String, state: Rc>) -> Self { Self { rl, prompt, state, cmd: "".to_string() } } - fn read(&mut self) -> LKEval { + pub fn read(&mut self) -> LKEval { self.cmd = match self.rl.readline(&*self.prompt) { Ok(str) => str, Err(err) => return LKEval::new(Command::Error(LKErr::ReadError(err.to_string())), self.state.clone()), }; match command_parser::cmd(self.cmd.as_str()) { Ok(cmd) => LKEval::new(cmd, self.state.clone()), - Err(err) => LKEval::new(Command::Error(LKErr::PegParseError(err)), self.state.clone()), + Err(err) => LKEval::new(Command::Error(LKErr::ParseError(err)), self.state.clone()), } } - fn refresh(&mut self) { + pub fn refresh(&mut self) { } - fn quit(&mut self) { + pub fn quit(&mut self) { } } impl<'a> LKEval<'a> { - fn new(cmd: Command<'a>, state: Rc>) -> Self { Self { cmd, state } } + pub fn new(cmd: Command<'a>, state: Rc>) -> Self { Self { cmd, state } } - fn eval(&mut self) -> LKPrint { + pub fn eval(&mut self) -> LKPrint { let mut out: Vec = vec![]; let mut quit: bool = false; @@ -95,25 +101,48 @@ impl<'a> LKEval<'a> { }, Command::Error(err) => { match err { - LKErr::PegParseError(e) => { out.push(e.to_string()) }, + LKErr::ParseError(e) => { out.push(e.to_string()) }, LKErr::ReadError(e) => { out.push(e.to_string()) }, LKErr::Error(e) => { out.push(format!("error: {}", e.to_string())) }, - _ => out.push(format!("error: {:?}", err)), } } } - LKPrint::new(out, quit) + LKPrint::new(out, quit, self.state.clone()) } } impl LKPrint { - fn new(out: Vec, quit: bool) -> Self { Self { out, quit } } + pub fn new(out: Vec, quit: bool, state: Rc>) -> Self { Self { out, quit, state } } - fn print(&mut self) -> bool { + pub fn print(&mut self) -> bool { for line in &self.out { println!("{}", line); } return !self.quit; } } + +#[cfg(test)] +mod tests { + use super::*; + use std::collections::HashMap; + use chrono::naive::NaiveDate; + use crate::structs::*; + + #[test] + fn exec_cmds_basic() { + let lk = Rc::new(RefCell::new(LK { db: HashMap::new() })); + assert_eq!(LKEval::new(Command::Ls, lk.clone()).eval(), LKPrint::new(vec![], false, lk.clone())); + let pwd = Rc::new(RefCell::new(Password { name: Rc::new("t1".to_string()), + prefix: None, + length: None, + mode: Mode::Regular, + seq: 99, + date: NaiveDate::from_ymd_opt(2022, 12, 30).unwrap(), + comment: Some("comment".to_string()), + parent: None })); + assert_eq!(LKEval::new(Command::Add(pwd.clone()), lk.clone()).eval().state.borrow().db, + { let mut db = HashMap::new(); db.insert(pwd.borrow().name.clone(), pwd.clone()); db }); + } +} \ No newline at end of file diff --git a/src/structs.rs b/src/structs.rs index 58e159a..60671e2 100644 --- a/src/structs.rs +++ b/src/structs.rs @@ -1,3 +1,4 @@ +use std::{cell::RefCell, rc::Rc}; use std::collections::HashMap; use regex::{Regex, Captures}; use chrono::naive::NaiveDate; @@ -8,12 +9,8 @@ pub enum LKErr<'a> { Error(&'a str), #[error("Failed to read the line: {0}")] ReadError(String), - #[error("Failed to parse {0}: {1}")] - ParseError(&'a str, &'a str), - #[error("Failed to parse {0}: {1}")] - ParseErrorS(&'a str, String), #[error("Failed to parse: {0}")] - PegParseError(peg::error::ParseError), + ParseError(peg::error::ParseError), } #[derive(PartialEq, Debug)] @@ -31,14 +28,14 @@ pub enum Mode { #[derive(PartialEq, Debug)] pub struct Password { - parent: Option>>, - prefix: Option, - name: Rc, - length: Option, - mode: Mode, - seq: u32, - date: NaiveDate, - comment: Option, + pub parent: Option>>, + pub prefix: Option, + pub name: Rc, + pub length: Option, + pub mode: Mode, + pub seq: u32, + pub date: NaiveDate, + pub comment: Option, } #[derive(PartialEq, Debug)] @@ -51,9 +48,9 @@ pub enum Command<'a> { Quit } -#[derive(Debug)] -struct LK { - db: HashMap, Rc>>, +#[derive(PartialEq, Debug)] +pub struct LK { + pub db: HashMap, Rc>>, } impl std::fmt::Display for Mode { @@ -73,7 +70,7 @@ impl std::fmt::Display for Mode { } impl LK { - fn fix_hierarchy(&self) { + pub fn fix_hierarchy(&self) { lazy_static! { static ref RE: Regex = Regex::new(r"\s*\^([!-~]+)").unwrap(); }