Split project into three workspaces: hel (the library), helcli (the tool) and helwasm (the wasm code). Move wasm incompatible code to extra modules in utils.rs to be implementable separately for wasm.

This commit is contained in:
Oleksandr Kozachuk 2023-01-01 18:50:16 +01:00
parent cff9e3f90d
commit eced302282
18 changed files with 330 additions and 181 deletions

View File

@ -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"]

31
hel/Cargo.toml Normal file
View File

@ -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"

View File

@ -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<PasswordRef> {
@ -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<PasswordRef> = 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);

15
hel/src/lib.rs Normal file
View File

@ -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;

View File

@ -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())
})
);

View File

@ -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<RefCell<Password>>;
pub type Parent = Option<PasswordRef>;
pub type Length = Option<u32>;
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");

View File

@ -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<RefCell<LK>>,
pub cmd: String,
@ -31,7 +30,7 @@ pub struct LKPrint {
}
impl LKRead {
pub fn new(rl: Editor<()>, prompt: String, state: Rc<RefCell<LK>>) -> Self {
pub fn new(rl: Editor, prompt: String, state: Rc<RefCell<LK>>) -> 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())),
},
}

View File

@ -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<Path> = {
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<Path> = {
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<Path> = {
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<Path> = {
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<LKRead> {
.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)]

179
hel/src/utils.rs Normal file
View File

@ -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<Self, &'static str> {
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<String, LKErr<'a>> {
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<String>, input: &str) -> io::Result<String> {
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<String>)> {
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<String>) {
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()
);
}
}

14
helcli/Cargo.toml Normal file
View File

@ -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"

12
helcli/src/bin/hel.rs Normal file
View File

@ -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();
}

16
helwasm/Cargo.toml Normal file
View File

@ -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"

8
helwasm/src/lib.rs Normal file
View File

@ -0,0 +1,8 @@
extern crate hel;
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub fn add(a: i32, b: i32) -> i32 {
a + b + 1
}

View File

@ -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();
}

View File

@ -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<String>, input: &str) -> io::Result<String> {
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<String>)> {
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<String>) {
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()
);
}
}