From e8a329be5d081f7a90f05aa8362742f75b6f16cf Mon Sep 17 00:00:00 2001
From: cophilot <philxsb@gmail.com>
Date: Mon, 12 Feb 2024 23:04:32 +0100
Subject: [PATCH] v0.4.0

---
 .gitignore             |  3 +-
 .phil-project          |  2 +-
 CHANGELOG.md           | 11 +++++++
 Cargo.lock             | 73 +++++++++++++++++++++++++++++++++++++++++-
 Cargo.toml             |  3 +-
 README.md              | 11 +++++--
 src/command_storage.rs |  4 +++
 src/commands.rs        | 25 ++++++++++-----
 src/data.rs            |  7 ++--
 src/utils.rs           | 55 ++++++++++++++++++++++++++++---
 10 files changed, 172 insertions(+), 22 deletions(-)

diff --git a/.gitignore b/.gitignore
index e6d0486..4e54f55 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,4 +4,5 @@ ideas.md
 .templify
 .templates
 templify-example
-install2
\ No newline at end of file
+install2
+dev/
\ No newline at end of file
diff --git a/.phil-project b/.phil-project
index da1396e..64ea341 100644
--- a/.phil-project
+++ b/.phil-project
@@ -1,4 +1,4 @@
 logo:assets/logo.png
 logo_small:assets/logo.png
 description_translate:de
-version:0.3.1
+version:0.4.0
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 83a8715..742aafc 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,17 @@
 
 ---
 
+## [v0.4.0](https://github.com/cophilot/templify/tree/0.4.0) (2024-2-12)
+
+-   `.templify` file is now optional
+-   Added `-blank` flag for the `init` command
+-   Added placeholder `$$year$$`
+-   Added placeholder `$$month$$`
+-   Added placeholder `$$day$$`
+-   Added placeholder `$$git-name$$`
+
+---
+
 ## [v0.3.1](https://github.com/cophilot/templify/tree/0.3.1) (2024-2-9)
 
 -   Added `-dry-run` flag for the `generate` command
diff --git a/Cargo.lock b/Cargo.lock
index e4e5286..b2c99ba 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -17,6 +17,21 @@ version = "1.0.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
 
+[[package]]
+name = "android-tzdata"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0"
+
+[[package]]
+name = "android_system_properties"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
+dependencies = [
+ "libc",
+]
+
 [[package]]
 name = "autocfg"
 version = "1.1.0"
@@ -83,6 +98,20 @@ version = "1.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
 
+[[package]]
+name = "chrono"
+version = "0.4.34"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5bc015644b92d5890fab7489e49d21f879d5c990186827d42ec511919404f38b"
+dependencies = [
+ "android-tzdata",
+ "iana-time-zone",
+ "js-sys",
+ "num-traits",
+ "wasm-bindgen",
+ "windows-targets 0.52.0",
+]
+
 [[package]]
 name = "core-foundation"
 version = "0.9.4"
@@ -325,6 +354,29 @@ dependencies = [
  "tokio-native-tls",
 ]
 
+[[package]]
+name = "iana-time-zone"
+version = "0.1.60"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141"
+dependencies = [
+ "android_system_properties",
+ "core-foundation-sys",
+ "iana-time-zone-haiku",
+ "js-sys",
+ "wasm-bindgen",
+ "windows-core",
+]
+
+[[package]]
+name = "iana-time-zone-haiku"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
+dependencies = [
+ "cc",
+]
+
 [[package]]
 name = "idna"
 version = "0.5.0"
@@ -449,6 +501,15 @@ dependencies = [
  "tempfile",
 ]
 
+[[package]]
+name = "num-traits"
+version = "0.2.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a"
+dependencies = [
+ "autocfg",
+]
+
 [[package]]
 name = "num_cpus"
 version = "1.16.0"
@@ -784,8 +845,9 @@ dependencies = [
 
 [[package]]
 name = "templify"
-version = "0.3.1"
+version = "0.4.0"
 dependencies = [
+ "chrono",
  "reqwest",
  "self-replace",
  "serde_json",
@@ -1006,6 +1068,15 @@ dependencies = [
  "wasm-bindgen",
 ]
 
+[[package]]
+name = "windows-core"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9"
+dependencies = [
+ "windows-targets 0.52.0",
+]
+
 [[package]]
 name = "windows-sys"
 version = "0.48.0"
diff --git a/Cargo.toml b/Cargo.toml
index a6b08e6..dd82925 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "templify"
-version = "0.3.1"
+version = "0.4.0"
 edition = "2021"
 
 # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@@ -9,3 +9,4 @@ edition = "2021"
 reqwest = { version = "0.11", features = ["blocking", "json"] }
 self-replace = "1.3.6"
 serde_json = "1.0.1"
+chrono = "0.4.19"
diff --git a/README.md b/README.md
index 7df6ced..252ef82 100644
--- a/README.md
+++ b/README.md
@@ -213,9 +213,14 @@ tpy load https://github.com/cophilot/templify-vault/tree/main/React-ts
 
 ## [Release Notes](https://github.com/cophilot/templify/blob/master/CHANGELOG.md)
 
-### [v0.3.1](https://github.com/cophilot/templify/tree/0.3.1)
-
--   Added `-dry-run` flag for the `generate` command
+### [v0.4.0](https://github.com/cophilot/templify/tree/0.4.0)
+
+-   `.templify` file is now optional
+-   Added `-blank` flag for the `init` command
+-   Added placeholder `$$year$$`
+-   Added placeholder `$$month$$`
+-   Added placeholder `$$day$$`
+-   Added placeholder `$$git-name$$`
 
 ---
 
diff --git a/src/command_storage.rs b/src/command_storage.rs
index 5349c20..6cf93cc 100644
--- a/src/command_storage.rs
+++ b/src/command_storage.rs
@@ -59,6 +59,10 @@ pub fn get_all_commands() -> Vec<Command> {
         vec!["offline".to_string(), "o".to_string()],
         "Do not fetch the example template from the internet.".to_string(),
     ));
+    init_com.add_flag(Flag::new_bool_flag(
+        vec!["blank".to_string(), "b".to_string()],
+        "Initialize only a blank .templates folder.".to_string(),
+    ));
 
     commands.push(init_com);
 
diff --git a/src/commands.rs b/src/commands.rs
index 6aa4a9c..27d5524 100644
--- a/src/commands.rs
+++ b/src/commands.rs
@@ -1,9 +1,9 @@
-use std::fs::read_dir;
-
 use crate::{
     types::{Command, Status},
     utils, version_control,
 };
+use chrono::{self, Datelike};
+use std::fs::read_dir;
 
 pub fn list(_command: &Command) -> Status {
     let st = utils::check_if_templify_initialized();
@@ -115,10 +115,15 @@ pub fn generate(command: &Command) -> Status {
 
     println!("Generating new files from template {}...", template_name);
 
-    let new_path = utils::parse_templify_file(&format!(".templates/{}/.templify", template_name))
-        ["path"]
-        .clone()
-        .replace("$$name$$", given_name.as_str());
+    let mut new_path =
+        utils::parse_templify_file(&format!(".templates/{}/.templify", template_name))["path"]
+            .clone();
+
+    new_path = new_path.replace("$$name$$", given_name.as_str());
+    new_path = new_path.replace("$$year$$", chrono::Local::now().year().to_string().as_str());
+    new_path = new_path.replace("$$month$$", &chrono::Local::now().month().to_string());
+    new_path = new_path.replace("$$day$$", &chrono::Local::now().day().to_string());
+    new_path = new_path.replace("$$git-name$$", &utils::get_git_name());
 
     // create dir and all subdirs if they don't exist
     if !dry_run {
@@ -205,11 +210,15 @@ pub fn init(command: &Command) -> Status {
 
     // check if .templates folder exists
     if std::path::Path::new(".templates").exists() {
-        println!("templify is already initialized in this project.");
-        return Status::ok();
+        return Status::error("templify is already initialized in this project.".to_string());
     }
 
     std::fs::create_dir(".templates").unwrap();
+
+    if command.get_bool_flag("blank") {
+        println!("templify initialized successfully.");
+        return Status::ok();
+    }
     std::fs::write(
         ".templates/README.md",
         crate::data::get_init_readme_content(),
diff --git a/src/data.rs b/src/data.rs
index 15bb685..21e978f 100644
--- a/src/data.rs
+++ b/src/data.rs
@@ -2,7 +2,8 @@ pub fn templify_file_blank(description: String, path: String) -> String {
     let content = format!("# This file is used by templify to generate new files from this template.
 # You can use the following variables in this file:
 # description:The description of the template 
-# path:The path of the template based on the project root (You can also use the placeholder $$name$$ to use the name of the template for the path)
+# path: The path where the file should be generated based on the project root (you can also use placeholders like $$name$$)
+
 description:{}
 path:{}
 ", description, path);
@@ -25,13 +26,13 @@ templify is a command line tool that helps you manage your project templates.
 
 ---
 
-To learn more about templify, please visit the [GitHub repository](https://github.com/cophilot/templify).
+To learn more about templify, please visit [templify.philipp-bonin.com](https://templify.philipp-bonin.com/).
 
 ---
 
 ## How to use templify?
 
-When you can see this file, you have already initialized templify in this folder. You can now create your own templates and generate new files from them.
+When you can see this file, you have already initialized templify in this project. You can now create your own templates and generate new files from them.
 
 ### Create a new template
 
diff --git a/src/utils.rs b/src/utils.rs
index e080d1b..d1be59c 100644
--- a/src/utils.rs
+++ b/src/utils.rs
@@ -1,3 +1,4 @@
+use chrono::{self, Datelike};
 use std::{io::Write, path::Path};
 
 use crate::types::Status;
@@ -8,7 +9,25 @@ pub fn parse_templify_file(file_path: &str) -> std::collections::HashMap<String,
     map.insert("description".to_string(), "".to_string());
     map.insert("path".to_string(), ".".to_string());
 
-    let file_content = std::fs::read_to_string(file_path).unwrap();
+    let file_content = std::fs::read_to_string(file_path);
+    if file_content.is_err() {
+        return map;
+    }
+    let file_content = file_content.unwrap();
+
+    let mut divider = ":".to_string();
+
+    let first_line = file_content.lines().next();
+    if first_line.is_none() {
+        return map;
+    }
+
+    let first_line = first_line.unwrap().replace(" ", "");
+    if first_line.starts_with("#!") {
+        let new_divider = first_line.clone().replace("#!", "");
+
+        divider = new_divider.to_string();
+    }
 
     for line in file_content.lines() {
         let line = line.trim();
@@ -16,7 +35,7 @@ pub fn parse_templify_file(file_path: &str) -> std::collections::HashMap<String,
             continue;
         }
 
-        let parts: Vec<&str> = line.split(":").collect();
+        let parts: Vec<&str> = line.split(divider.as_str()).collect();
         if parts.len() < 2 {
             continue;
         }
@@ -114,7 +133,13 @@ pub fn generate_template_dir(path: &str, new_path: &str, given_name: &str, dry_r
             continue;
         }
 
-        let new_file_name = file_name.replace("$$name$$", given_name);
+        let mut new_file_name = file_name.replace("$$name$$", given_name);
+        new_file_name =
+            new_file_name.replace("$$year$$", chrono::Local::now().year().to_string().as_str());
+        new_file_name =
+            new_file_name.replace("$$month$$", &chrono::Local::now().month().to_string());
+        new_file_name = new_file_name.replace("$$day$$", &chrono::Local::now().day().to_string());
+        new_file_name = new_file_name.replace("$$git-name$$", &crate::utils::get_git_name());
         let new_path = format!("{}/{}", new_path, new_file_name);
 
         // check if new_path already exists
@@ -139,7 +164,12 @@ pub fn generate_template_dir(path: &str, new_path: &str, given_name: &str, dry_r
 
 pub fn generate_template_file(path: &str, new_path: &str, given_name: &str, dry_run: bool) -> bool {
     let file_content = std::fs::read_to_string(path).unwrap();
-    let file_content = file_content.replace("$$name$$", given_name);
+    let mut file_content = file_content.replace("$$name$$", given_name);
+    file_content =
+        file_content.replace("$$year$$", chrono::Local::now().year().to_string().as_str());
+    file_content = file_content.replace("$$month$$", &chrono::Local::now().month().to_string());
+    file_content = file_content.replace("$$day$$", &chrono::Local::now().day().to_string());
+    file_content = file_content.replace("$$git-name$$", &crate::utils::get_git_name());
 
     if Path::new(new_path).exists() {
         println!("File {} already exists.", new_path);
@@ -177,3 +207,20 @@ pub fn check_internet_connection() -> bool {
     }
     return false;
 }
+
+pub fn get_git_name() -> String {
+    let output = std::process::Command::new("git")
+        .arg("config")
+        .arg("user.name")
+        .output();
+    if output.is_err() {
+        return "unknown".to_string();
+    }
+    let output = output.unwrap();
+    let output = String::from_utf8_lossy(&output.stdout);
+    let mut name = output.trim().to_string();
+    if name.is_empty() {
+        name = "unknown".to_string();
+    }
+    return name;
+}