diff --git a/Cargo.toml b/Cargo.toml
index f9c9e77..cde3a91 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -18,8 +18,9 @@ path="src/cli/main.rs"
chrono = "0.4.34"
clap = { version = "3.2.17", features = ["derive"] }
colored = "2.0.0"
-dirs = "5.0.1"
+indexmap = "2.2.6"
promptly = "0.3.1"
regex = "1.7.1"
serde = { version = "1.0.152", features = ["serde_derive"] }
+shellexpand = "3.1.0"
toml = "0.5.9"
diff --git a/README.md b/README.md
index 93fc85b..e19ca2d 100644
--- a/README.md
+++ b/README.md
@@ -55,6 +55,9 @@ note that the template `info` section can be totally ignored, straight to the po
-->
Default templates path is `~/.config/idkmng/templates`
+> [!NOTE]
+> You can use -c option to override the config path if you needed to.
+
The template structure is like the following:
```toml
[info]
@@ -236,7 +239,8 @@ Also there is one more time saving way! if you have some files in `/foo/bar/` yo
## Special Keywords 🔧
You can have your own Keywords for idkmng to replace with desired values!
-Idkmng finds them stored in $HOME/.config/idkmng/config.toml
+Idkmng finds them stored in $HOME/.config/idkmng/config.toml Or the config path you specified using -c/--config option 🦀
+
```toml
[Keywords]
AUTHOR = "Mohamed Tarek"
diff --git a/src/cli/args.rs b/src/cli/args.rs
index 1ffe030..842cde8 100644
--- a/src/cli/args.rs
+++ b/src/cli/args.rs
@@ -20,6 +20,14 @@ impl Cli {
.short('q')
.requires("template"),
)
+ .arg(
+ Arg::with_name("config")
+ .long("config")
+ .short('c')
+ .help("Config path")
+ .default_value("~/.config/idkmng/config.toml")
+ .requires("template")
+ )
.subcommand(Command::new("init").about("Creates a template for the current directory"))
.get_matches()
}
diff --git a/src/cli/main.rs b/src/cli/main.rs
index 8c6e8d4..367b8cc 100644
--- a/src/cli/main.rs
+++ b/src/cli/main.rs
@@ -1,3 +1,4 @@
+use idkmng::config::Config;
use idkmng::types::Template;
mod args;
use args::Cli;
@@ -6,6 +7,8 @@ use colored::*;
fn main() {
let args = Cli::parse();
+ let config = Config::new(args.value_of("config").unwrap());
+
if args.subcommand_matches("init").is_some() {
let dest = format!(
"{}.toml",
@@ -19,13 +22,13 @@ fn main() {
println!("{}: {}", "Creating Template".bold().green(), &dest.yellow());
Template::generate(&dest);
} else if let Some(filename) = args.value_of("template") {
- let template = Template::validate(filename.to_string());
+ let template = Template::validate(filename.to_string(), config.templates_path.clone());
println!("\n{}: {}", "Using Template".blue(), &template.magenta());
if !args.is_present("quiet") {
Template::show_info(&Template::parse(&template, true));
}
- Template::extract(template, true);
+ Template::extract(template, true, config);
} else {
println!(
"{} {}",
diff --git a/src/core/config.rs b/src/core/config.rs
index c00b642..dfee3d1 100644
--- a/src/core/config.rs
+++ b/src/core/config.rs
@@ -1,20 +1,33 @@
use crate::keywords::Keywords;
use crate::types::Template;
-use crate::utils::gethome;
use std::collections::HashMap;
use std::fs;
use toml::Value;
-pub const CONFIG_PATH: &str = "{{$HOME}}/.config/idkmng/config.toml";
-pub const TEMPLATES_PATH: &str = "{{$HOME}}/.config/idkmng/templates/";
pub const KEYWORDS_FORMAT: &str = "{{$%s:f}}";
pub const KEYWORDS_REGEX: &str = r"\{\{\$[^\s}]+(:[^\s}]+)?\}\}";
pub struct Config {
pub path: String,
+ pub templates_path: String,
}
impl Config {
+ pub fn new(path: &str) -> Self {
+ let config_path = shellexpand::tilde(path).to_string();
+ let mut config_dir: Vec<&str> = path.split("/").collect();
+
+ config_dir.pop();
+
+ //NOTE: maybe templates path should be parsed from config.toml itself??
+ let templates = config_dir.join("/") + "/templates/";
+
+ Config {
+ path: config_path,
+ templates_path: shellexpand::tilde(&templates).to_string(),
+ }
+ }
+
pub fn init(self) {
// this sample is just a template that create config.toml and the new.toml template for the
// first time, Now something maybe confusing is the "initPJNAME" wtf is it ?
@@ -29,7 +42,7 @@ description = "A Template for making a template"
author = "Mohamed Tarek @pwnxpl0it"
[[files]]
-path="~/.config/idkmng/templates/initPJNAME.toml"
+path="TEMPLATES_PATH/initPJNAME.toml"
content="""
[info]
name = "initPJNAME"
@@ -51,12 +64,9 @@ content = '''
'''
"#
.replace("CONFIGPATH", &self.path)
- .replace(
- "TEMPLATES_PATH",
- &TEMPLATES_PATH.replace("{{$HOME}}", &gethome()),
- );
+ .replace("TEMPLATES_PATH", &self.templates_path);
- Template::extract(sample, false);
+ Template::extract(sample, false, self);
}
pub fn get_keywords(self) -> HashMap {
diff --git a/src/core/keywords.rs b/src/core/keywords.rs
index 3d4d407..8b9f574 100644
--- a/src/core/keywords.rs
+++ b/src/core/keywords.rs
@@ -1,5 +1,4 @@
-use crate::config::{Config, CONFIG_PATH, KEYWORDS_FORMAT};
-use crate::utils::gethome;
+use crate::config::{Config, KEYWORDS_FORMAT};
use chrono::Datelike;
use std::{collections::HashMap, env};
@@ -20,7 +19,7 @@ impl Keywords {
}
}
- pub fn init() -> HashMap {
+ pub fn init(config: Config) -> HashMap {
let mut keywords = HashMap::new();
keywords.insert(
Self::new(String::from("HOME"), "".to_string()),
@@ -71,10 +70,7 @@ impl Keywords {
chrono::Local::now().day().to_string(),
);
- let other_keywords = Config {
- path: CONFIG_PATH.replace("{{$HOME}}", &gethome()),
- }
- .get_keywords();
+ let other_keywords = config.get_keywords();
keywords.extend(other_keywords);
keywords
diff --git a/src/core/templates.rs b/src/core/templates.rs
index 55b87f3..0f14366 100644
--- a/src/core/templates.rs
+++ b/src/core/templates.rs
@@ -52,11 +52,11 @@ impl Template {
}
/// This method "extracts" a template, means it takes a template and starts initializing files based that template
- pub fn extract(template: String, is_file: bool) {
+ pub fn extract(template: String, is_file: bool, config: Config) {
let mut keywords: HashMap;
let re = Regex::new(KEYWORDS_REGEX).unwrap();
- keywords = Keywords::init();
+ keywords = Keywords::init(config);
let sample = Self::parse(&template, is_file);
@@ -77,17 +77,14 @@ impl Template {
let path = Keywords::replace_keywords(keywords.to_owned(), file.path.to_owned());
if dir.len() > 1 {
- create_dirs(
- &Keywords::replace_keywords(
- keywords.to_owned(),
- file.path.to_owned().replace(dir[dir.len() - 1], ""),
- )
- .replace('~', &gethome()),
- )
+ create_dirs(&shellexpand::tilde(&Keywords::replace_keywords(
+ keywords.to_owned(),
+ file.path.to_owned().replace(dir[dir.len() - 1], ""),
+ )))
}
write_content(
- &path.replace('~', &gethome()),
+ &shellexpand::tilde(&path),
Keywords::replace_keywords(keywords.to_owned(), file.content),
)
});
@@ -108,9 +105,9 @@ impl Template {
toml::from_str(&content).unwrap()
}
- /// This method validates Template path, in other words it just checks if the template is in
- /// the current working Directory,if not it uses the default templates directory, also automatically adds .toml
- pub fn validate(mut template: String) -> String {
+ /// This method validates template path, in other words it just checks if the template is in
+ /// the current working directory,if not it uses the default templates directory, also automatically adds .toml
+ pub fn validate(mut template: String, template_path: String) -> String {
if template.contains(".toml") {
//IGNORE
} else {
@@ -120,14 +117,14 @@ impl Template {
if fs::read_to_string(&template).is_ok() {
//IGNORE
} else {
- template = TEMPLATES_PATH.replace("{{$HOME}}", &gethome()) + &template
+ template = template_path + &template
}
template
}
- /// This method shows information about current Template, basically Reads them from Information
- /// section in the Template TOML file
+ /// This method shows information about current template, basically Reads them from Information
+ /// section in the template TOML file
pub fn show_info(template: &Self) {
match &template.info {
Some(information) => println!(
diff --git a/src/core/utils.rs b/src/core/utils.rs
index 40a99bf..d7adc13 100644
--- a/src/core/utils.rs
+++ b/src/core/utils.rs
@@ -1,17 +1,8 @@
use crate::types::Fns;
use colored::*;
-use dirs;
use regex::Regex;
use std::{collections::HashMap, fs, path::Path};
-pub fn gethome() -> String {
- dirs::home_dir()
- .expect("Failed to know home directory")
- .to_str()
- .unwrap()
- .to_string()
-}
-
pub fn create_dirs(dir: &str) {
match fs::create_dir_all(dir) {
Ok(_) => println!("{}: {}", "creating directory".blue(), dir.bold().green()),