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
/// Internal macro for common logging logic
#[macro_export]
#[doc(hidden)]
macro_rules! __internal_log_impl {
// Standard variant - uses ABSOLUTE_PATH_TO_EVENFRAME env var
($content:expr, $log_subdir:expr, standard) => {{
let filename = format!("{}.log", chrono::Local::now().format("%Y_%m_%d_%H_%M_%S"));
let logs_dir = format!(
"{}/{}",
std::env::var("ABSOLUTE_PATH_TO_EVENFRAME")
.expect("ABSOLUTE_PATH_TO_EVENFRAME not set"),
$log_subdir
);
$crate::__internal_log_impl!($content, logs_dir, filename, false, impl);
}};
($content:expr, $log_subdir:expr, $filename:expr, standard) => {{
let logs_dir = format!(
"{}/{}",
std::env::var("ABSOLUTE_PATH_TO_EVENFRAME")
.expect("ABSOLUTE_PATH_TO_EVENFRAME not set"),
$log_subdir
);
$crate::__internal_log_impl!($content, logs_dir, $filename, false, impl);
}};
($content:expr, $log_subdir:expr, $filename:expr, $append:expr, standard) => {{
let logs_dir = format!(
"{}/{}",
std::env::var("ABSOLUTE_PATH_TO_EVENFRAME")
.expect("ABSOLUTE_PATH_TO_EVENFRAME not set"),
$log_subdir
);
$crate::__internal_log_impl!($content, logs_dir, $filename, $append, impl);
}};
// Core implementation
($content:expr, $logs_dir:expr, $filename:expr, $append:expr, impl) => {{
use std::io::Write;
// Create logs directory if it doesn't exist
let _ = std::fs::create_dir_all(&$logs_dir);
let path_str = &format!("{}/{}", $logs_dir, $filename);
let path = std::path::Path::new(path_str);
let mut options = std::fs::OpenOptions::new();
options.create(true);
if $append {
options.append(true);
} else {
options.write(true).truncate(true);
}
if let Ok(mut file_handle) = options.open(path) {
// Check if the expression is a format! macro or string literal
let expr_str = stringify!($content);
let formatted = if expr_str.starts_with("format!")
|| expr_str.starts_with("&format!")
|| expr_str.starts_with("\"")
|| expr_str.starts_with("String::")
{
// For formatted strings, just output the content directly
format!("{}\n", $content)
} else if $filename.ends_with(".surql") {
// For .surql files, output the content as a plain string without debug formatting
format!("{}\n", $content)
} else {
// For other types, use debug output with location info
let value_str = format!("{:#?}", &$content);
// Check if it's a multi-line value
if value_str.contains('\n') || value_str.len() > 80 {
format!(
"[{}:{}] {} = \n{}\n",
file!(),
line!(),
stringify!($content),
value_str
)
} else {
format!(
"[{}:{}] {} = {}\n",
file!(),
line!(),
stringify!($content),
value_str
)
}
};
let _ = file_handle.write_all(formatted.as_bytes());
}
}};
}
/// Logging macro for the evenframe crate.
///
/// # Examples
///
/// Log to a timestamp-based file (e.g., "2024_01_12_14_30_52.log"):
/// ```no_run
/// # use evenframe_core::evenframe_log;
/// evenframe_log!("Sync started");
/// ```
///
/// Log to a specific file (overwrites existing content):
/// ```no_run
/// # use evenframe_core::evenframe_log;
/// evenframe_log!("Types generated", "output.log");
/// ```
///
/// Log to a specific file with append mode:
/// ```no_run
/// # use evenframe_core::evenframe_log;
/// evenframe_log!("New type added", "output.log", true);
/// ```
#[macro_export]
macro_rules! evenframe_log {
($content:expr) => {{
$crate::__internal_log_impl!($content, "evenframe/logs", standard);
}};
($content:expr, $filename:expr) => {{
$crate::__internal_log_impl!($content, "evenframe/logs", $filename, standard);
}};
($content:expr, $filename:expr, $append:expr) => {{
$crate::__internal_log_impl!($content, "evenframe/logs", $filename, $append, standard);
}};
}