enigma_3d/logging/
mod.rs

1pub mod format;
2
3use std::env;
4use colored::Colorize;
5use chrono::Local;
6use std::fs::{self, OpenOptions};
7use std::io::Write;
8use std::path::PathBuf;
9use once_cell::sync::Lazy;
10
11const LOG_DIRECTORY: &str = "enigma_logs";
12static LOG_FILE_PATH: Lazy<PathBuf> = Lazy::new(|| initialize_log_file());
13
14fn initialize_log_file() -> PathBuf {
15    let exec_name = env::current_exe()
16        .ok()
17        .and_then(|pb| pb.file_name().map(|s| s.to_string_lossy().into_owned()))
18        .unwrap_or_else(|| "unknown".to_string());
19
20    let datetime = Local::now().format("%Y-%m-%d_%H-%M-%S");
21    let filename = format!("{}_{}.log", exec_name, datetime);
22    PathBuf::from(LOG_DIRECTORY).join(filename)
23}
24
25fn get_log_filepath() -> PathBuf {
26    // Simply clone the lazy initialized path
27    LOG_FILE_PATH.clone()
28}
29
30fn save_to_disk(log: Box<&dyn EnigmaLog>) -> std::io::Result<()> {
31    let prefix = match log.log_type() {
32        EnigmaLogType::Error => "ERROR >> ",
33        EnigmaLogType::Warning => "WARNING >> ",
34        EnigmaLogType::Message => "MESSAGE >> ",
35        EnigmaLogType::Unknown => ">> ",
36    };
37
38    let joined_string = log.get()
39        .iter()
40        .map(|s| format!("{}{}", prefix, s))
41        .collect::<Vec<String>>()
42        .join("\n");
43
44    // Ensure the directory exists
45    fs::create_dir_all(LOG_DIRECTORY)?;
46
47    let log_path = get_log_filepath();
48    let mut file = OpenOptions::new()
49        .create(true)
50        .append(true)
51        .open(log_path)?;
52
53    writeln!(file, "{}", joined_string)?;
54
55    Ok(())
56}
57
58#[derive(Debug, PartialEq)]
59pub enum EnigmaLogType {
60    Error,
61    Warning,
62    Message,
63    Unknown,
64}
65
66pub trait EnigmaLog {
67    fn get(&self) -> &Vec<String>;
68    fn log_type(&self) -> EnigmaLogType;
69}
70
71impl EnigmaLog for EnigmaError {
72    fn get(&self) -> &Vec<String> {
73        &self.errors
74    }
75    fn log_type(&self) -> EnigmaLogType {
76        EnigmaLogType::Error
77    }
78}
79impl EnigmaLog for EnigmaWarning {
80    fn get(&self) -> &Vec<String> {
81        &self.warnings
82    }
83    fn log_type(&self) -> EnigmaLogType {
84        EnigmaLogType::Warning
85    }
86}
87impl EnigmaLog for EnigmaMessage {
88    fn get(&self) -> &Vec<String> {
89        &self.messages
90    }
91    fn log_type(&self) -> EnigmaLogType {
92        EnigmaLogType::Message
93    }
94}
95
96
97#[derive(Debug)]
98pub struct EnigmaError {
99    errors: Vec<String>,
100    disk: bool
101}
102
103#[derive(Debug)]
104pub struct EnigmaWarning {
105    warnings: Vec<String>,
106    disk: bool
107}
108
109#[derive(Debug)]
110pub struct EnigmaMessage {
111    messages: Vec<String>,
112    disk: bool
113}
114
115impl EnigmaError {
116    pub fn new(error: Option<&str>, disk: bool) -> Self {
117        Self {
118            errors: match error {
119                Some(e) => vec![e.to_string()],
120                None => Vec::new()
121            },
122            disk
123        }
124    }
125
126    pub fn extent(&mut self, error: &str) {
127        self.errors.push(error.to_string());
128    }
129
130    pub fn log(&self) {
131        for error in self.errors.iter() {
132            println!("{} {}","ERROR >>".red(), error.red());
133        }
134        if self.disk {
135            save_to_disk(Box::new(self)).expect("failed to write log")
136        }
137    }
138
139    pub fn merge(&mut self, error: EnigmaError) {
140        for e in error.errors {
141            self.extent(e.as_str())
142        }
143    }
144
145    pub fn is_empty(&self) -> bool {
146        self.errors.is_empty()
147    }
148}
149
150impl EnigmaWarning {
151    pub fn new(warning: Option<&str>, disk: bool) -> Self {
152        Self {
153            warnings: match warning {
154                Some(w) => vec![w.to_string()],
155                None => Vec::new()
156            },
157            disk
158        }
159    }
160
161    pub fn extent(&mut self, warning: &str) {
162        self.warnings.push(warning.to_string());
163    }
164
165    pub fn log(&self) {
166        for warning in self.warnings.iter() {
167            println!("{} {}","WARNING >>".yellow(), warning.yellow());
168        }
169        if self.disk {
170            save_to_disk(Box::new(self)).expect("failed to write log")
171        }
172    }
173
174    pub fn merge(&mut self, error: EnigmaWarning) {
175        for w in error.warnings {
176            self.extent(w.as_str())
177        }
178    }
179
180    pub fn is_empty(&self) -> bool {
181        self.warnings.is_empty()
182    }
183}
184
185impl EnigmaMessage {
186    pub fn new(message: Option<&str>, disk: bool) -> Self {
187        Self {
188            messages: match message {
189                Some(m) => vec![m.to_string()],
190                None => Vec::new()
191            },
192            disk
193        }
194    }
195
196    pub fn extent(&mut self, message: &str) {
197        self.messages.push(message.to_string());
198    }
199
200    pub fn log(&self) {
201        for message in self.messages.iter() {
202            println!("{} {}","MESSAGE >>".bright_blue(), message.bright_blue());
203        }
204        if self.disk {
205            save_to_disk(Box::new(self)).expect("failed to write log")
206        }
207    }
208
209    pub fn merge(&mut self, error: EnigmaMessage) {
210        for m in error.messages {
211            self.extent(m.as_str())
212        }
213    }
214
215    pub fn is_empty(&self) -> bool {
216        self.messages.is_empty()
217    }
218}