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 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 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}