use std::{collections::HashMap, env};
use regex::Regex;
pub use crate::{
envfile::{load_env, Section},
utils::to_bool,
};
mod envfile;
mod utils;
#[derive(Debug, PartialEq, PartialOrd)]
pub enum ArgType {
Hyphen,
DoubeHyPhen,
}
#[derive(Debug)]
pub struct EasyArg {
config: HashMap<String, String>,
invalid: Vec<String>,
_desc: HashMap<String, (ArgType, String)>,
_alias: HashMap<String, String>,
sections: Vec<Section>,
_section_key: Option<String>,
_sub_section_key: Option<String>,
}
impl EasyArg {
pub fn new() -> EasyArg {
EasyArg::new_with_env(".easy_arg")
}
pub fn new_with_env(env_name: &str) -> EasyArg {
let mut config: HashMap<String, String> = HashMap::new();
let mut invalid: Vec<String> = vec![];
fn add_key_value(conf: &mut HashMap<String, String>, key: &str, value: &str) {
let key = key.replacen("-", "", 2);
conf.insert(key, value.to_string());
}
fn make_invalid(conf: &mut Vec<String>, key: String) {
conf.push(key);
}
let has_key = Regex::new(r"^--\w").expect("create -- regex");
let has_flag = Regex::new(r"^-\w").expect("create - regex");
let has_value = Regex::new(r"=").expect("create = regex");
let mut key = String::new();
for argument in env::args().skip(1) {
if argument == "--" || argument == "-" {
if has_flag.is_match(&argument) {
add_key_value(&mut config, &key.to_string(), "true");
}
continue;
}
let is_eq = has_value.is_match(&argument);
if has_key.is_match(&argument) || has_flag.is_match(&argument) {
if key.len() > 0 {
if has_key.is_match(&key) {
make_invalid(&mut invalid, key)
} else if has_flag.is_match(&key) {
add_key_value(&mut config, &key.to_string(), "true");
}
key = "".to_string();
}
if is_eq {
let keyval: Vec<&str> = argument.split("=").collect();
add_key_value(&mut config, keyval[0], keyval[1]);
} else {
key = argument;
}
} else {
if key.len() > 0 {
add_key_value(&mut config, &key, &argument);
key = "".to_string();
}
}
}
if key.len() > 0 {
if has_key.is_match(&key) {
make_invalid(&mut invalid, key);
} else if has_flag.is_match(&key) {
add_key_value(&mut config, &key.to_string(), "true")
}
}
let mut easy = EasyArg {
config,
sections: vec![],
invalid,
_desc: HashMap::new(),
_alias: HashMap::new(),
_section_key: None,
_sub_section_key: None,
};
easy.alias("e", "envfile");
easy.alias("h", "help");
easy.desc_flag("help", "show help");
let env_file = easy.get_string("envfile");
if env_file.len() > 0 {
let (mut env_config, secs) = load_env(&env_file);
env_config.extend(easy.config.clone());
easy.config = env_config;
easy.sections = secs;
} else {
match EasyArg::load_env(env_name) {
Some((mut val, secs)) => {
val.extend(easy.config.clone());
easy.config = val;
easy.sections = secs;
}
None => {}
}
}
return easy;
}
fn load_env(env_name: &str) -> Option<(HashMap<String, String>, Vec<Section>)> {
match env::current_dir() {
Ok(pt) => {
let env_file = format!("{}/{}.env", pt.display(), env_name);
if std::path::Path::new(&env_file).exists() {
return Some(load_env(&env_file));
}
let env_file = format!("{}/.env", pt.display());
if std::path::Path::new(&env_file).exists() {
return Some(load_env(&env_file));
}
let home = env::var("HOME").expect("home");
let env_file = format!("{}/{}.env", home, env_name);
if std::path::Path::new(&env_file).exists() {
return Some(load_env(&env_file));
} else {
return None;
}
}
Err(err) => {
panic!("{:?}", err);
}
}
}
pub fn use_section(&mut self, key: &str) {
if key.len() == 0 {
self._section_key = None;
} else {
self._section_key = Some(key.to_string());
}
}
pub fn use_sub_section(&mut self, sub_key: &str) {
if self._section_key.is_none() {
return;
}
if sub_key.len() == 0 {
self._sub_section_key = None;
} else {
self._sub_section_key = Some(sub_key.to_string());
}
}
pub fn raw_value(&self, key: &str) -> Option<String> {
let mut real_key = key;
if !self.config.contains_key(key) {
for (new_key, old_key) in self._alias.iter() {
if new_key == key {
real_key = old_key
} else if old_key == key {
real_key = new_key
}
}
}
if let Some(val) = self.config.get(real_key) {
return Some(val.to_string());
} else {
let mut section: Option<&Section> = None;
if let Some(sec_key) = self._section_key.as_ref() {
if let Some(sub_key) = self._sub_section_key.as_ref() {
for sec in self.sections.iter() {
if sec.key == *sub_key {
if let Some(this_sec_key) = sec.parent.as_ref() {
if this_sec_key == sec_key {
section = Some(sec);
}
}
}
}
} else {
for sec in self.sections.iter() {
if sec.key == *sec_key {
section = Some(sec);
}
}
}
}
if let Some(sec) = section {
if let Some(val) = sec.data.get(real_key) {
return Some(val.to_string());
}
}
let val = env::var_os(real_key);
if let Some(val) = val {
let val2 = val.to_str().expect("os str to str");
return Some(val2.to_string());
} else {
return None;
}
}
}
pub fn get_string(&self, key: &str) -> String {
match self.raw_value(key) {
Some(val) => val,
None => "".to_string(),
}
}
pub fn must_get_string(&self, key: &str) -> String {
match self.raw_value(key) {
Some(val) => val,
None => {
panic!("{} doesn't exist", key)
}
}
}
pub fn get_bool(&self, key: &str) -> bool {
let val = self.raw_value(key);
match val {
Some(val) => to_bool(&val),
None => false,
}
}
pub fn get_vec(&self, key: &str) -> Vec<String> {
if key.len() == 0 {
return vec![];
}
let val = self.raw_value(key);
match val {
Some(val) => val.split(",").map(|s| s.to_string()).collect(),
None => vec![],
}
}
pub fn alias(&mut self, source_key: &str, new_key: &str) {
let new_key = new_key.to_string();
self._alias.entry(new_key).or_insert(source_key.to_string());
}
pub fn get_invalid(&self) -> &Vec<String> {
return &self.invalid;
}
pub fn desc_str(&mut self, key: &str, description: &str) {
self._desc.entry(key.to_string()).or_insert((ArgType::DoubeHyPhen, description.to_string()));
}
pub fn desc_flag(&mut self, key: &str, description: &str) {
self._desc.entry(key.to_string()).or_insert((ArgType::Hyphen, description.to_string()));
}
pub fn show_help(&self) -> String {
let mut help = format!("\nExecutable: {:?}\n", env::args().into_iter().next().unwrap()).replace("\"", "");
let mut desc: Vec<(&String, &(ArgType, String))> = self._desc.iter().map(|f| f).collect();
desc.sort_by(|a, b| b.1 .0.partial_cmp(&a.1 .0).expect("compare"));
for (index, (key, value)) in desc.iter().enumerate() {
let c = match value.0 {
ArgType::Hyphen => "-",
ArgType::DoubeHyPhen => "--",
};
let mut spliter = "\n";
if index >= 1 {
if desc[index - 1].1 .0 != desc[index].1 .0 {
spliter = "\n\n";
}
}
let row = format!("{} {: >3} {: <10} {}", spliter, c, key, value.1);
help += row.as_str();
}
return help.to_string();
}
pub fn get_section(&self, key: &str) -> Option<Section> {
for sec in self.sections.iter() {
if sec.key == key {
return Some(sec.clone());
}
}
return None;
}
pub fn get_sub_section(&self, key: &str, sub_key: &str) -> Option<Section> {
for sec in self.sections.iter() {
if sec.key == sub_key {
if let Some(parent) = sec.parent.as_ref() {
if parent == key {
return Some(sec.clone());
}
}
}
}
return None;
}
}
#[test]
fn write_test() {
}