diff --git a/Cargo.toml b/Cargo.toml index 8e4641f..a1b5377 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,25 +1,2 @@ -[package] -name = "hel" -version = "0.1.0" -authors = ["ok2"] -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -chrono = "0.4.23" -peg = "0.8.1" -anyhow = "1.0.66" -lazy_static = "1.4.0" -regex = "1.6.0" -rustyline = "10.0.0" -thiserror = "1.0.37" -anyerror = "0.1.7" -home = "0.5.4" -sha1 = "0.10.5" -base64 = "0.20.0" -rpassword = "7.2.0" -shlex = "1.1.0" -shellexpand = "3.0.0" -scopeguard = "1.1.0" -rand = "0.8.5" +[workspace] +members = ["hel", "helcli", "helwasm"] diff --git a/hel/Cargo.toml b/hel/Cargo.toml new file mode 100644 index 0000000..76ae296 --- /dev/null +++ b/hel/Cargo.toml @@ -0,0 +1,31 @@ +[package] +name = "hel" +version = "0.1.0" +authors = ["ok2"] +edition = "2021" + +[lib] +name = "hel" +crate-type = ["lib"] + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +peg = "0.8.1" +anyhow = "1.0.66" +lazy_static = "1.4.0" +regex = "1.6.0" +thiserror = "1.0.37" +anyerror = "0.1.7" +sha1 = "0.10.5" +base64 = "0.20.0" +rpassword = "7.2.0" +shlex = "1.1.0" +shellexpand = "3.0.0" +scopeguard = "1.1.0" + +[target.'cfg(not(wasm))'.dependencies] +chrono = "0.4.23" +rand = "0.8.5" +home = "0.5.4" +rustyline = "10.0.0" diff --git a/src/commands.rs b/hel/src/commands.rs similarity index 99% rename from src/commands.rs rename to hel/src/commands.rs index 657686f..0fe7362 100644 --- a/src/commands.rs +++ b/hel/src/commands.rs @@ -1,4 +1,3 @@ -use rand::{thread_rng, Rng}; use regex::Regex; use rpassword::prompt_password; use sha1::{Digest, Sha1}; @@ -13,7 +12,7 @@ use crate::password::fix_password_recursion; use crate::password::{Name, Password, PasswordRef}; use crate::repl::LKEval; use crate::structs::{LKOut, Radix, CORRECT_FILE, DUMP_FILE}; -use crate::utils::{call_cmd_with_input, get_cmd_args_from_command, get_copy_command_from_env}; +use crate::utils::{call_cmd_with_input, get_cmd_args_from_command, get_copy_command_from_env, rnd}; impl<'a> LKEval<'a> { pub fn get_password(&self, name: &String) -> Option { @@ -429,7 +428,6 @@ impl<'a> LKEval<'a> { lazy_static! { static ref RE: Regex = Regex::new(r"^.+?(G+|X+)$").unwrap(); } - let mut rng = thread_rng(); let num: usize = (*num).try_into().unwrap(); let pwd = name.borrow(); let mut genpwds: Vec = Vec::new(); @@ -445,7 +443,7 @@ impl<'a> LKEval<'a> { } } else { let name = pwd.name.trim_end_matches('X'); - let num = rng.gen_range(1..10_u32.pow(gen.len().try_into().unwrap())); + let num = rnd::range(1, 10_u32.pow(gen.len().try_into().unwrap())); let npwd = Password::from_password(&pwd); npwd.borrow_mut().name = format!("{}{}", name, num).to_string(); genpwds.push(npwd); diff --git a/hel/src/lib.rs b/hel/src/lib.rs new file mode 100644 index 0000000..f9c8565 --- /dev/null +++ b/hel/src/lib.rs @@ -0,0 +1,15 @@ +#[macro_use] +extern crate lazy_static; +#[allow(unused_imports)] +#[macro_use(defer)] +extern crate scopeguard; + +pub mod commands; +pub mod lk; +pub mod parser; +pub mod password; +pub mod repl; +pub mod skey; +pub mod structs; +pub mod utils; + diff --git a/src/lk.rs b/hel/src/lk.rs similarity index 100% rename from src/lk.rs rename to hel/src/lk.rs diff --git a/src/parser.rs b/hel/src/parser.rs similarity index 88% rename from src/parser.rs rename to hel/src/parser.rs index 714a98f..5595b01 100644 --- a/src/parser.rs +++ b/hel/src/parser.rs @@ -2,8 +2,7 @@ extern crate peg; use crate::password::Password; use crate::structs::{Command, LKErr, Mode}; -use chrono::naive::NaiveDate; -use chrono::Local; +use crate::utils::date::Date; use std::{cell::RefCell, rc::Rc}; peg::parser! { @@ -42,21 +41,21 @@ peg::parser! { rule sname() -> Password = &(word() _ num()? mode() _ date()) pn:word() _ pl:num()? pm:mode() _ pd:date() pc:comment()? { Password::new(None, pn, pl, pm, 99, pd, pc) } rule nname() -> Password = &(word() _ num()? mode()) pn:word() _ pl:num()? pm:mode() - { Password::new(None, pn, pl, pm, 99, Local::now().naive_local().date(), None) } + { Password::new(None, pn, pl, pm, 99, Date::now(), None) } rule qname() -> Password = &(word()) pn:word() - { Password::new(None, pn, None, Mode::NoSpaceCamel, 99, Local::now().naive_local().date(), None) } + { Password::new(None, pn, None, Mode::NoSpaceCamel, 99, Date::now(), None) } pub rule name() -> Password = name:(jname() / pname() / mname() / sname() / nname() / qname())? {? match name { Some(n) => Ok(n), None => Err("failed to parse password description") } } - rule ndate() -> NaiveDate = y:$("-"? ['0'..='9']*<1,4>) "-" m:$(['0'..='9']*<1,2>) "-" d:$(['0'..='9']*<1,2>) {? + rule ndate() -> Date = y:$("-"? ['0'..='9']*<1,4>) "-" m:$(['0'..='9']*<1,2>) "-" d:$(['0'..='9']*<1,2>) {? let year: i32 = match y.parse() { Ok(n) => n, Err(_) => return Err("year") }; let month: u32 = match m.parse() { Ok(n) => n, Err(_) => return Err("month") }; let day: u32 = match d.parse() { Ok(n) => n, Err(_) => return Err("day") }; - NaiveDate::from_ymd_opt(year, month, day).ok_or("date") + Date::try_new(year, month, day) } - rule cdate() -> NaiveDate = "now" { Local::now().naive_local().date() } - rule date() -> NaiveDate = d:(ndate() / cdate()) { d } + rule cdate() -> Date = "now" { Date::now() } + rule date() -> Date = d:(ndate() / cdate()) { d } rule umode() -> Mode = ("U" / "u") m:$("R" / "r" / "N" / "n" / "H" / "h" / "B" / "b") {? match m.to_uppercase().as_str() { "R" => Ok(Mode::RegularUpcase), @@ -126,7 +125,7 @@ add t3 C 99 2022-12-14"### length: None, mode: Mode::NoSpaceCamel, seq: 99, - date: NaiveDate::from_ymd_opt(2022, 12, 14).unwrap(), + date: Date::new(2022, 12, 14), comment: None }))), Command::Add(Rc::new(RefCell::new(Password { @@ -136,7 +135,7 @@ add t3 C 99 2022-12-14"### length: None, mode: Mode::NoSpaceCamel, seq: 99, - date: NaiveDate::from_ymd_opt(2022, 12, 14).unwrap(), + date: Date::new(2022, 12, 14), comment: None }))), Command::Add(Rc::new(RefCell::new(Password { @@ -146,7 +145,7 @@ add t3 C 99 2022-12-14"### length: None, mode: Mode::NoSpaceCamel, seq: 99, - date: NaiveDate::from_ymd_opt(2022, 12, 14).unwrap(), + date: Date::new(2022, 12, 14), comment: None }))) ]) @@ -166,7 +165,7 @@ add t3 C 99 2022-12-14 length: None, mode: Mode::NoSpaceCamel, seq: 99, - date: NaiveDate::from_ymd_opt(2022, 12, 14).unwrap(), + date: Date::new(2022, 12, 14), comment: None }))), Command::Add(Rc::new(RefCell::new(Password { @@ -176,7 +175,7 @@ add t3 C 99 2022-12-14 length: None, mode: Mode::NoSpaceCamel, seq: 99, - date: NaiveDate::from_ymd_opt(2022, 12, 14).unwrap(), + date: Date::new(2022, 12, 14), comment: None }))), Command::Add(Rc::new(RefCell::new(Password { @@ -186,7 +185,7 @@ add t3 C 99 2022-12-14 length: None, mode: Mode::NoSpaceCamel, seq: 99, - date: NaiveDate::from_ymd_opt(2022, 12, 14).unwrap(), + date: Date::new(2022, 12, 14), comment: None }))), Command::Noop @@ -208,7 +207,7 @@ add t3 C 99 2022-12-14 length: None, mode: Mode::NoSpaceCamel, seq: 99, - date: NaiveDate::from_ymd_opt(2022, 12, 14).unwrap(), + date: Date::new(2022, 12, 14), comment: None }))), Command::Add(Rc::new(RefCell::new(Password { @@ -218,7 +217,7 @@ add t3 C 99 2022-12-14 length: None, mode: Mode::NoSpaceCamel, seq: 99, - date: NaiveDate::from_ymd_opt(2022, 12, 14).unwrap(), + date: Date::new(2022, 12, 14), comment: None }))), Command::Add(Rc::new(RefCell::new(Password { @@ -228,7 +227,7 @@ add t3 C 99 2022-12-14 length: None, mode: Mode::NoSpaceCamel, seq: 99, - date: NaiveDate::from_ymd_opt(2022, 12, 14).unwrap(), + date: Date::new(2022, 12, 14), comment: None }))), Command::Noop, @@ -248,7 +247,7 @@ add t3 C 99 2022-12-14 mode: Mode::Regular, length: None, seq: 99, - date: NaiveDate::from_ymd_opt(2020, 12, 09).unwrap(), + date: Date::new(2020, 12, 09), comment: Some("xx.ableton@domain.info https://www.ableton.com".to_string()) }) ); @@ -261,7 +260,7 @@ add t3 C 99 2022-12-14 mode: Mode::RegularUpcase, length: None, seq: 99, - date: NaiveDate::from_ymd_opt(2020, 12, 09).unwrap(), + date: Date::new(2020, 12, 09), comment: Some("xx.ableton@domain.info https://www.ableton.com".to_string()) }) ); @@ -274,7 +273,7 @@ add t3 C 99 2022-12-14 mode: Mode::RegularUpcase, length: None, seq: 99, - date: NaiveDate::from_ymd_opt(2020, 12, 09).unwrap(), + date: Date::new(2020, 12, 09), comment: None }) ); @@ -287,7 +286,7 @@ add t3 C 99 2022-12-14 mode: Mode::Regular, length: None, seq: 99, - date: NaiveDate::from_ymd_opt(2020, 12, 09).unwrap(), + date: Date::new(2020, 12, 09), comment: Some("xx.ableton@domain.info https://www.ableton.com".to_string()) }) ); @@ -300,7 +299,7 @@ add t3 C 99 2022-12-14 mode: Mode::NoSpace, length: None, seq: 99, - date: NaiveDate::from_ymd_opt(2020, 12, 09).unwrap(), + date: Date::new(2020, 12, 09), comment: Some("xx.ableton@domain.info https://www.ableton.com".to_string()) }) ); @@ -313,7 +312,7 @@ add t3 C 99 2022-12-14 mode: Mode::NoSpaceUpcase, length: None, seq: 99, - date: NaiveDate::from_ymd_opt(2020, 12, 09).unwrap(), + date: Date::new(2020, 12, 09), comment: Some("xx.ableton@domain.info https://www.ableton.com".to_string()) }) ); @@ -326,7 +325,7 @@ add t3 C 99 2022-12-14 mode: Mode::Regular, length: Some(20), seq: 99, - date: NaiveDate::from_ymd_opt(2020, 12, 09).unwrap(), + date: Date::new(2020, 12, 09), comment: Some("a b c".to_string()) }) ); @@ -339,7 +338,7 @@ add t3 C 99 2022-12-14 mode: Mode::RegularUpcase, length: Some(20), seq: 99, - date: NaiveDate::from_ymd_opt(2020, 12, 09).unwrap(), + date: Date::new(2020, 12, 09), comment: Some("a b c".to_string()) }) ); @@ -352,7 +351,7 @@ add t3 C 99 2022-12-14 mode: Mode::HexUpcase, length: Some(20), seq: 99, - date: NaiveDate::from_ymd_opt(2020, 12, 09).unwrap(), + date: Date::new(2020, 12, 09), comment: Some("a b c".to_string()) }) ); @@ -365,7 +364,7 @@ add t3 C 99 2022-12-14 mode: Mode::Base64Upcase, length: Some(20), seq: 99, - date: NaiveDate::from_ymd_opt(2020, 12, 09).unwrap(), + date: Date::new(2020, 12, 09), comment: Some("a b c".to_string()) }) ); @@ -378,7 +377,7 @@ add t3 C 99 2022-12-14 mode: Mode::Decimal, length: Some(20), seq: 99, - date: NaiveDate::from_ymd_opt(2020, 12, 09).unwrap(), + date: Date::new(2020, 12, 09), comment: Some("a b c".to_string()) }) ); @@ -391,7 +390,7 @@ add t3 C 99 2022-12-14 mode: Mode::Decimal, length: Some(20), seq: 98, - date: NaiveDate::from_ymd_opt(2020, 12, 09).unwrap(), + date: Date::new(2020, 12, 09), comment: Some("a b c".to_string()) }) ); @@ -404,7 +403,7 @@ add t3 C 99 2022-12-14 mode: Mode::NoSpaceCamel, length: Some(20), seq: 98, - date: NaiveDate::from_ymd_opt(2020, 12, 09).unwrap(), + date: Date::new(2020, 12, 09), comment: Some("a b c".to_string()) }) ); @@ -417,7 +416,7 @@ add t3 C 99 2022-12-14 mode: Mode::Decimal, length: Some(20), seq: 99, - date: NaiveDate::from_ymd_opt(2020, 12, 09).unwrap(), + date: Date::new(2020, 12, 09), comment: Some("a b c".to_string()) }) ); diff --git a/src/password.rs b/hel/src/password.rs similarity index 95% rename from src/password.rs rename to hel/src/password.rs index 5e89ac5..9a7376f 100644 --- a/src/password.rs +++ b/hel/src/password.rs @@ -1,6 +1,6 @@ use crate::skey::SKey; use crate::structs::Mode; -use chrono::naive::NaiveDate; +use crate::utils::date::Date; use std::{cell::RefCell, rc::Rc}; pub type Name = String; @@ -10,7 +10,6 @@ pub type PasswordRef = Rc>; pub type Parent = Option; pub type Length = Option; pub type Seq = u32; -pub type Date = NaiveDate; #[derive(PartialEq, Debug)] pub struct Password { @@ -173,7 +172,7 @@ mod tests { None, Mode::Regular, 99, - NaiveDate::from_ymd_opt(2022, 12, 3).unwrap(), + Date::new(2022, 12, 3), None, ))); @@ -187,7 +186,7 @@ mod tests { None, Mode::Regular, 99, - NaiveDate::from_ymd_opt(2022, 12, 3).unwrap(), + Date::new(2022, 12, 3), None, ))); p2.borrow_mut().parent = Some(p1.clone()); @@ -197,7 +196,7 @@ mod tests { None, Mode::Regular, 99, - NaiveDate::from_ymd_opt(2022, 12, 3).unwrap(), + Date::new(2022, 12, 3), None, ))); p3.borrow_mut().parent = Some(p2.clone()); @@ -207,7 +206,7 @@ mod tests { None, Mode::Regular, 99, - NaiveDate::from_ymd_opt(2022, 12, 3).unwrap(), + Date::new(2022, 12, 3), None, ))); p4.borrow_mut().parent = Some(p3.clone()); @@ -217,7 +216,7 @@ mod tests { None, Mode::Regular, 99, - NaiveDate::from_ymd_opt(2022, 12, 3).unwrap(), + Date::new(2022, 12, 3), None, ))); p5.borrow_mut().parent = Some(p4.clone()); @@ -230,7 +229,7 @@ mod tests { #[test] fn exec_encode_test() { let sec = "my secret"; - let dat = NaiveDate::from_ymd_opt(2022, 12, 3).unwrap(); + let dat = Date::new(2022, 12, 3); let mut pwd = Password::new(None, "test1".to_string(), None, Mode::Regular, 99, dat, None); assert_eq!(pwd.encode(sec), "ross beau week held yoga anti"); diff --git a/src/repl.rs b/hel/src/repl.rs similarity index 98% rename from src/repl.rs rename to hel/src/repl.rs index 039c027..a105a36 100644 --- a/src/repl.rs +++ b/hel/src/repl.rs @@ -1,15 +1,14 @@ use rpassword::prompt_password; -use rustyline::error::ReadlineError; -use rustyline::Editor; use std::{cell::RefCell, rc::Rc}; use crate::lk::LK; use crate::parser::command_parser; use crate::structs::{Command, LKErr, LKOut, HISTORY_FILE}; +use crate::utils::editor::Editor; #[derive(Debug)] pub struct LKRead { - pub rl: Editor<()>, + pub rl: Editor, pub prompt: String, pub state: Rc>, pub cmd: String, @@ -31,7 +30,7 @@ pub struct LKPrint { } impl LKRead { - pub fn new(rl: Editor<()>, prompt: String, state: Rc>) -> Self { + pub fn new(rl: Editor, prompt: String, state: Rc>) -> Self { Self { rl, prompt, @@ -53,7 +52,7 @@ impl LKRead { } self.cmd = match self.rl.readline(&*self.prompt) { Ok(str) => str, - Err(ReadlineError::Eof | ReadlineError::Interrupted) => "quit".to_string(), + Err(LKErr::EOF) => "quit".to_string(), Err(err) => { return LKEval::new( Command::Error(LKErr::ReadError(err.to_string())), @@ -128,6 +127,7 @@ impl<'a> LKEval<'a> { 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())), }, } diff --git a/src/skey.rs b/hel/src/skey.rs similarity index 100% rename from src/skey.rs rename to hel/src/skey.rs diff --git a/src/structs.rs b/hel/src/structs.rs similarity index 96% rename from src/structs.rs rename to hel/src/structs.rs index 768d5c5..2a7b022 100644 --- a/src/structs.rs +++ b/hel/src/structs.rs @@ -1,7 +1,5 @@ use crate::password::{Comment, Name, PasswordRef}; -use home::home_dir; use rpassword::prompt_password; -use rustyline::Editor; use std::fmt; use std::path::Path; use std::{cell::RefCell, rc::Rc}; @@ -9,12 +7,14 @@ use std::{cell::RefCell, rc::Rc}; use crate::lk::LK; use crate::parser::command_parser; use crate::repl::{LKEval, LKRead}; +use crate::utils::home; +use crate::utils::editor::Editor; lazy_static! { pub static ref HISTORY_FILE: Box = { 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(".hel_history").into_boxed_path(), + _ => home::dir().join(".hel_history").into_boxed_path(), } }; pub static ref PROMPT_SETTING: String = { @@ -26,19 +26,19 @@ lazy_static! { pub static ref INIT_FILE: Box = { 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(".helrc").into_boxed_path(), + _ => home::dir().join(".helrc").into_boxed_path(), } }; pub static ref CORRECT_FILE: Box = { 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(".hel_correct").into_boxed_path(), + _ => home::dir().join(".hel_correct").into_boxed_path(), } }; pub static ref DUMP_FILE: Box = { 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(".hel_dump").into_boxed_path(), + _ => home::dir().join(".hel_dump").into_boxed_path(), } }; } @@ -47,6 +47,8 @@ lazy_static! { pub enum LKErr<'a> { #[error("Error: {0}")] Error(&'a str), + #[error("Error: end of file")] + EOF, #[error("Failed to read the line: {0}")] ReadError(String), #[error("Failed to parse: {0}")] @@ -288,7 +290,7 @@ pub fn init() -> Option { .print(); } } - Some(LKRead::new(Editor::<()>::new().unwrap(), PROMPT_SETTING.to_string(), lk.clone())) + Some(LKRead::new(Editor::new(), PROMPT_SETTING.to_string(), lk.clone())) } #[cfg(test)] diff --git a/hel/src/utils.rs b/hel/src/utils.rs new file mode 100644 index 0000000..55583f4 --- /dev/null +++ b/hel/src/utils.rs @@ -0,0 +1,179 @@ +use shlex::split; +use std::env; +use std::ffi::OsString; +use std::io; +use std::io::{Read, Write}; +use std::process::{Command, Stdio}; + +#[cfg(not(wasm))] +pub mod date { + use chrono::naive::NaiveDate; + use chrono::Local; + + #[derive(PartialEq, Debug, Clone)] + pub struct Date { + date: NaiveDate + } + + impl Date { + pub fn new(year: i32, month: u32, day: u32) -> Self { + Self { date: NaiveDate::from_ymd_opt(year, month, day).unwrap() } + } + + pub fn try_new(year: i32, month: u32, day: u32) -> Result { + match NaiveDate::from_ymd_opt(year, month, day) { + Some(d) => Ok(Self { date: d }), + None => Err("error: failed to parse the date"), + } + } + + pub fn now() -> Self { + Self { date: Local::now().naive_local().date() } + } + + pub fn cmp(&self, other: &Self) -> core::cmp::Ordering { + self.date.cmp(&other.date) + } + } + + impl std::fmt::Display for Date { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.date.to_string()) + } + } +} + +#[cfg(not(wasm))] +pub mod rnd { + use rand::{thread_rng, Rng}; + + pub fn range(start: u32, end: u32) -> u32 { + thread_rng().gen_range(start..end) + } +} + +#[cfg(unix)] +pub mod home { + use home::home_dir; + use std::path::PathBuf; + + pub fn dir() -> PathBuf { + home_dir().unwrap() + } +} + +#[cfg(unix)] +pub mod editor { + use crate::structs::LKErr; + use rustyline::error::ReadlineError; + + #[derive(Debug)] + pub struct Editor { + editor: rustyline::Editor<()>, + } + + impl Editor { + pub fn new() -> Self { + Self { editor: rustyline::Editor::<()>::new().unwrap() } + } + + pub fn clear_history(&mut self) { + self.editor.clear_history(); + } + + pub fn add_history_entry(&mut self, entry: &str) { + self.editor.add_history_entry(entry); + } + + pub fn load_history<'a>(&mut self, fname: &str) -> Result<(), LKErr<'a>> { + match self.editor.load_history(&fname) { + Ok(_) => Ok(()), + Err(_) => Err(LKErr::Error("failed to read history file")), + } + } + + pub fn save_history<'a>(&mut self, fname: &str) -> Result<(), LKErr<'a>> { + match self.editor.save_history(&fname) { + Ok(_) => Ok(()), + Err(ReadlineError::Eof | ReadlineError::Interrupted) => Err(LKErr::EOF), + Err(_) => Err(LKErr::Error("failed to write history file")), + } + } + + pub fn readline<'a>(&mut self, prompt: &str) -> Result> { + match self.editor.readline(&prompt) { + Ok(line) => Ok(line), + Err(_) => Err(LKErr::Error("failed to read from input")), + } + } + } +} + +pub fn call_cmd_with_input(cmd: &str, args: &Vec, input: &str) -> io::Result { + let mut cmd = Command::new(cmd).args(args).stdin(Stdio::piped()).stdout(Stdio::piped()).spawn()?; + let mut stdin = cmd.stdin.take().unwrap(); + let stdout = cmd.stdout.as_mut().unwrap(); + let in_data = input.to_string(); + let write_handle = std::thread::spawn(move || stdin.write_all(in_data.as_bytes())); + let mut output = Vec::new(); + stdout.read_to_end(&mut output)?; + match write_handle.join() { + Ok(_) => (), + Err(_) => return Err(io::Error::new(io::ErrorKind::Other, "Failed to run command")), + } + match String::from_utf8(output) { + Ok(x) => Ok(x), + Err(err) => Err(io::Error::new(io::ErrorKind::InvalidData, err.utf8_error())), + } +} + +pub fn get_cmd_args_from_command(command: &str) -> io::Result<(String, Vec)> { + let args = match split(command) { + Some(c) => c, + None => { + return Err(io::Error::new( + io::ErrorKind::InvalidData, + format!("Failed to parse the command: {:?}", command), + )) + } + }; + Ok((shellexpand::full(&args[0]).unwrap().into_owned(), args[1..].to_vec())) +} + +pub fn get_copy_command_from_env() -> (String, Vec) { + 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"), + _ => OsString::from("cat"), + }); + get_cmd_args_from_command(&cmd_os_str.to_string_lossy()).unwrap_or_else(|_| ("cat".to_string(), vec![])) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn cmd_exec_test() { + assert_eq!(call_cmd_with_input("true", &vec![], "").unwrap(), "".to_string()); + assert_eq!(call_cmd_with_input("cat", &vec![], "ok").unwrap(), "ok".to_string()); + assert_eq!( + call_cmd_with_input( + "cat", + &vec![], + r###"line 1 +line 2 +line 3 +line 4"### + ) + .unwrap(), + "line 1\nline 2\nline 3\nline 4".to_string() + ); + assert_ne!(call_cmd_with_input("cat", &vec![], "notok").unwrap(), "ok".to_string()); + assert_eq!( + call_cmd_with_input("echo", &vec!["-n".to_string(), "test is ok".to_string()], "").unwrap(), + "test is ok".to_string() + ); + } +} diff --git a/helcli/Cargo.toml b/helcli/Cargo.toml new file mode 100644 index 0000000..2ab0085 --- /dev/null +++ b/helcli/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "helcli" +version = "0.1.0" +authors = ["ok2"] +edition = "2021" + +[[bin]] +name = "hel" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +hel = { version = "0.1.0", path = "../hel" } +rustyline = "10.0.0" diff --git a/helcli/src/bin/hel.rs b/helcli/src/bin/hel.rs new file mode 100644 index 0000000..afd9424 --- /dev/null +++ b/helcli/src/bin/hel.rs @@ -0,0 +1,12 @@ +extern crate hel; + +use hel::structs::init; + +pub fn main() { + let mut lkread = match init() { Some(r) => r, None => { return; } }; + + while lkread.read().eval().print() { + lkread.refresh(); + } + lkread.quit(); +} diff --git a/helwasm/Cargo.toml b/helwasm/Cargo.toml new file mode 100644 index 0000000..ef00f95 --- /dev/null +++ b/helwasm/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "helwasm" +version = "0.1.0" +authors = ["ok2"] +edition = "2021" + +[target.'cfg(wasm32)'] +[lib] +name = "helwasm" +path = "src/lib.rs" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +hel = { version = "0.1.0", path = "../hel" } +wasm-bindgen = "0.2.83" diff --git a/html/index.html b/helwasm/index.html similarity index 100% rename from html/index.html rename to helwasm/index.html diff --git a/helwasm/src/lib.rs b/helwasm/src/lib.rs new file mode 100644 index 0000000..77a4405 --- /dev/null +++ b/helwasm/src/lib.rs @@ -0,0 +1,8 @@ +extern crate hel; + +use wasm_bindgen::prelude::*; + +#[wasm_bindgen] +pub fn add(a: i32, b: i32) -> i32 { + a + b + 1 +} diff --git a/src/main.rs b/src/main.rs deleted file mode 100644 index a6afb9e..0000000 --- a/src/main.rs +++ /dev/null @@ -1,26 +0,0 @@ -#![recursion_limit = "1024"] -#[macro_use] -extern crate lazy_static; -#[allow(unused_imports)] -#[macro_use(defer)] -extern crate scopeguard; - -mod commands; -mod lk; -mod parser; -mod password; -mod repl; -mod skey; -mod structs; -mod utils; - -use crate::structs::init; - -pub fn main() { - let mut lkread = match init() { Some(r) => r, None => { return; } }; - - while lkread.read().eval().print() { - lkread.refresh(); - } - lkread.quit(); -} diff --git a/src/utils.rs b/src/utils.rs deleted file mode 100644 index ea8d751..0000000 --- a/src/utils.rs +++ /dev/null @@ -1,75 +0,0 @@ -use shlex::split; -use std::env; -use std::ffi::OsString; -use std::io; -use std::io::{Read, Write}; -use std::process::{Command, Stdio}; - -pub fn call_cmd_with_input(cmd: &str, args: &Vec, input: &str) -> io::Result { - let mut cmd = Command::new(cmd).args(args).stdin(Stdio::piped()).stdout(Stdio::piped()).spawn()?; - let mut stdin = cmd.stdin.take().unwrap(); - let stdout = cmd.stdout.as_mut().unwrap(); - let in_data = input.to_string(); - let write_handle = std::thread::spawn(move || stdin.write_all(in_data.as_bytes())); - let mut output = Vec::new(); - stdout.read_to_end(&mut output)?; - match write_handle.join() { - Ok(_) => (), - Err(_) => return Err(io::Error::new(io::ErrorKind::Other, "Failed to run command")), - } - match String::from_utf8(output) { - Ok(x) => Ok(x), - Err(err) => Err(io::Error::new(io::ErrorKind::InvalidData, err.utf8_error())), - } -} - -pub fn get_cmd_args_from_command(command: &str) -> io::Result<(String, Vec)> { - let args = match split(command) { - Some(c) => c, - None => { - return Err(io::Error::new( - io::ErrorKind::InvalidData, - format!("Failed to parse the command: {:?}", command), - )) - } - }; - Ok((shellexpand::full(&args[0]).unwrap().into_owned(), args[1..].to_vec())) -} - -pub fn get_copy_command_from_env() -> (String, Vec) { - 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"), - _ => OsString::from("cat"), - }); - get_cmd_args_from_command(&cmd_os_str.to_string_lossy()).unwrap_or_else(|_| ("cat".to_string(), vec![])) -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn cmd_exec_test() { - assert_eq!(call_cmd_with_input("true", &vec![], "").unwrap(), "".to_string()); - assert_eq!(call_cmd_with_input("cat", &vec![], "ok").unwrap(), "ok".to_string()); - assert_eq!( - call_cmd_with_input( - "cat", - &vec![], - r###"line 1 -line 2 -line 3 -line 4"### - ) - .unwrap(), - "line 1\nline 2\nline 3\nline 4".to_string() - ); - assert_ne!(call_cmd_with_input("cat", &vec![], "notok").unwrap(), "ok".to_string()); - assert_eq!( - call_cmd_with_input("echo", &vec!["-n".to_string(), "test is ok".to_string()], "").unwrap(), - "test is ok".to_string() - ); - } -}