tiny_tools/tiny_macros.rs
1use serde::{Deserialize, Serialize};
2
3#[derive(Debug, Serialize, Deserialize)]
4pub struct Log<F, L> {
5    pub file: F,      // Source file where the log is generated
6    pub line: L,      // Line number where the log is generated
7    pub time: String, // Timestamp for the log entry
8    pub msg: String,  // Log message
9}
10
11// Macro to create and write log entries
12#[macro_export]
13macro_rules! json_logger {
14    ($msg:expr) => {{
15        use chrono::Local;
16        use std::fs::OpenOptions;
17        use std::io::Write;
18
19        // Get the current timestamp
20        let timestamp = Local::now().format("%Y-%m-%d %H:%M:%S").to_string();
21
22        // Create a log entry
23        let log_entry = $crate::tiny_macros::Log {
24            file: file!(),
25            line: line!(),
26            time: timestamp,
27            msg: $msg.to_string(),
28        };
29
30        // Serialize the log entry to JSON
31        let json_log = serde_json::to_string_pretty(&log_entry).unwrap();
32
33        let bin_name = env!("CARGO_PKG_NAME");
34        let log_dir = dirs::cache_dir()
35            .unwrap_or(dirs::data_dir().unwrap())
36            .join(bin_name);
37        if !log_dir.exists() {
38            std::fs::create_dir_all(&log_dir).unwrap();
39        }
40        let log_file = &log_dir.join("log.json");
41        if !log_file.exists() {
42            std::fs::write(log_file, "").unwrap();
43        }
44
45        // Define the log file path
46        let log_file_path = log_file; // Customize the log file path as needed
47
48        // Open the log file in append mode
49        let mut file = OpenOptions::new()
50            .create(true) // Create the file if it doesn't exist
51            .append(true) // Append to the file if it exists
52            .open(log_file_path)
53            .unwrap(); // Handle errors appropriately
54
55        // Write the JSON-formatted log entry to the file
56        writeln!(file, "{}", json_log).unwrap(); // Handle errors appropriately
57    }};
58}
59
60#[macro_export]
61#[cfg(feature = "debug")]
62macro_rules! dprintln {
63    ($($arg:tt)*) => (println!($($arg)*));
64}
65
66#[macro_export]
67#[cfg(not(feature = "debug"))]
68macro_rules! dprintln {
69    ($($arg:tt)*) => {};
70}
71
72#[macro_export]
73#[cfg(feature = "debug")]
74macro_rules! dprint {
75    ($($arg:tt)*) => (print!($($arg)*));
76}
77
78#[macro_export]
79#[cfg(not(feature = "debug"))]
80macro_rules! dprint {
81    ($($arg:tt)*) => {};
82}
83
84/// # verbose
85#[macro_export]
86macro_rules! verbose {
87    ($verbose_flag:expr, $($arg:tt)*) => {
88        if $verbose_flag {
89        println!($($arg)*);
90        }
91    };
92}
93
94#[macro_export]
95macro_rules! dev_debug {
96($verbose_flag:expr, $($arg:tt)*) => {
97    if $verbose_flag {
98    println!("[{}:{}] {}", file!(), line!(), format!($($arg)*));
99    }
100};
101}
102
103/// Show an error to stderr in a similar style to GNU coreutils.
104///
105/// Takes a [`format!`]-like input and prints it to stderr. The output is
106/// prepended with the current utility's name.
107#[macro_export]
108macro_rules! show_error(
109    ($($args:tt)+) => ({
110        eprint!("{}: ", env!("CARGO_PKG_NAME"));
111        eprintln!($($args)+);
112    })
113);
114
115// Prompt the user with a formatted string and returns `true` if they reply `'y'` or `'Y'`
116//
117// This macro functions accepts the same syntax as `format!`. The prompt is written to
118// `stderr`. A space is also printed at the end for nice spacing between the prompt and
119// the user input. Any input starting with `'y'` or `'Y'` is interpreted as `yes`.
120//#[macro_export]
121//macro_rules! prompt_yes(
122//    ($($args:tt)+) => ({
123//        use std::io::Write;
124//        eprint!("roxide: ");
125//        eprint!($($args)+);
126//        eprint!(" ");
127//    //    uucore::crash_if_err!(1, std::io::stderr().flush());
128//        uucore::read_yes()
129//    })
130//);
131
132/// Macro to prompt the user with a message and collect input.
133/// Returns `true` if the input is "yes" or "y" (case-insensitive), otherwise `false`.
134///
135/// Example usage:
136/// ```
137/// if prompt_yes!("Do you want to continue? (yes/y):") {
138///     println!("Continuing...");
139/// } else {
140///     println!("Exiting...");
141/// }
142/// ```
143#[macro_export]
144macro_rules! prompt_yes {
145    ($($arg:tt)*) => {{
146        use std::io::{self, Write};
147        // Print the prompt and flush stdout
148        print!("{}: ", env!("CARGO_PKG_NAME"));
149        print!($($arg)*);
150        print!(" "); // Add a space after the prompt
151        io::stdout().flush().unwrap();
152
153        // Read input from stdin
154        let mut input = String::new();
155        io::stdin().read_line(&mut input).unwrap();
156
157        // Trim and check for "yes" or "y" (case-insensitive)
158        matches!(input.trim().to_lowercase().as_str(), "yes" | "y")
159    }};
160}