1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
//! A collection of utility functions for common development tasks, including logging, terminal manipulation, file handling, and more.
//! 
//! ```toml
//! [dependencies]
//! dev_utils = "0.*"  # Add the latest version of this crate
//! log = "0.*"  # This crate also depends on the log crate, so add it too
//! ```
//! 
//! # Usage
//! 
//! ```rust 
//! use dev_utils::log::print_app_data;
//! use dev_utils::log::rlog::RLog;  // Logger (RLog) from this crate
//! use log::LevelFilter;  // Log crate
//! 
//! fn main() {
//!     print_app_data(file!());  // Print application data (name, version, authors, etc.)
//!     RLog::init_logger(LevelFilter::Trace);  // Initialize the logger with the given log level
//!     log::info!("Some data!");  // [2021-01-01 00:00:00] INFO: Hello World!
//!     // Your code here...
//! }
//! ```
#![allow(unused)]


use std::path::Path;
use std::fs;

pub mod log;
// pub mod http;
// pub mod files;
pub mod console;
pub mod conversion;

/// Clears the terminal screen and extracts relevant information from the 'Cargo.toml' file,
/// then prints this information in a structured format.
///
/// This function clears the terminal screen using ANSI escape codes and reads the 'Cargo.toml' file
/// to retrieve information such as the application name, version, and authors. It then prints this
/// information to the terminal.
///
/// # Examples
///
/// To use this function in your Rust application, you can import it and call it within your `main` function:
///
/// ```rust
/// use dev_utils::print_app_data;
///
/// fn main() {
///     print_app_data(file!());  // Print application data
/// }
/// ```
///
/// The function will clear the terminal and display information extracted from 'Cargo.toml', such as:
///
/// ```plaintext
/// MyApplicationName V0.1.0
/// Authors: Some Author, Another Author
/// ```

pub fn print_app_data(
    actual_file_path: &'static str
) {
    print!("\x1B[2J \x1B[1;1H");  // Clear the terminal screen

    let mut path = Path::new(actual_file_path);

    let file_name = "Cargo.toml";
    // let file_name = "TheFile.toml";

    let mut cargo_toml = String::new();
    let mut toml_is_found = false;
    
    while !toml_is_found {
        path = path.parent().unwrap();  // Go to the parent directory
        // println!("\npath: {}", path.display());

        if path.to_str().unwrap() == "" {
            // print all files in the current directory
            // fs::read_dir(".").unwrap().for_each(|entry| println!("\tfile: {}", entry.unwrap().path().display()));
            cargo_toml = fs::read_to_string(file_name).unwrap();
            break;
        };

        for entry in fs::read_dir(path).unwrap() {
            let path = entry.unwrap().path();
            // println!("\tfile: {}", path.display());
            if path.file_name().unwrap().to_str().unwrap() == file_name {
                cargo_toml = fs::read_to_string(path).unwrap();
                toml_is_found = true;
            }
        }

    }

    let app_data = extract_app_data_with_sections!(cargo_toml, "package" => ["name", "version", "authors"]);
    println!("{} v{}\n",
        console::format::set_fg(app_data.get("package").unwrap().get("name").unwrap().to_string(), 'g'),
        console::format::set_fg(app_data.get("package").unwrap().get("version").unwrap().to_string(), 'b'),
    );

}


#[macro_export]
macro_rules! extract_app_data_with_sections {
    ($data:expr, $($section:expr => [$($key:expr),+]),+) => {{
        let mut app_data: std::collections::HashMap<&str, std::collections::HashMap<&str, &str>> = std::collections::HashMap::new();
        let mut current_section = "";

        for line in $data.lines() {
            if line.starts_with("[") && line.ends_with("]") {
                current_section = line.trim_matches(&['[', ']'][..]);
            } else {
                $(
                    if current_section == $section {
                        $(
                            if line.contains($key) {
                                if let Some(index) = line.find('=') {
                                    let value = line[(index + 1)..].trim().trim_matches('"');
                                    app_data.entry($section).or_insert_with(std::collections::HashMap::new).insert($key, value);
                                }
                            }
                        )*
                    }
                )*
            }
        }
        app_data
    }};
}


// * Same as the macro above, but with a different syntax
// * The main difference is that the macro above uses the `+` operator to match one or more keys, while this one uses the `*` operator to match zero or more keys
// fn extract_app_data_with_sections(data: &str) -> std::collections::HashMap<String, std::collections::HashMap<String, String>> {
//     let mut app_data = std::collections::HashMap::new();
//     let mut current_section = String::new();

//     for line in data.lines() {
//         if line.starts_with("[") && line.ends_with("]") {
//             current_section = line.trim_matches(&['[', ']'][..]).to_string();
//         } else if let Some(index) = line.find('=') {
//             let key = line[..index].trim();
//             let value = line[(index + 1)..].trim().trim_matches('"');
//             app_data.entry(current_section.clone())
//                     .or_insert_with(std::collections::HashMap::new)
//                     .insert(key.to_string(), value.to_string());
//         }
//     }

//     app_data
// }