1use std::{
88 any::type_name,
89 collections::HashMap,
90 hash::Hash,
91 sync::RwLock,
92 time::{Duration, SystemTime, UNIX_EPOCH},
93};
94
95#[derive(Debug, Clone, Copy, PartialEq, Eq)]
96pub enum LogLevel {
97 Error,
98 Debug,
99 Info,
100 Warn,
101}
102
103struct Logger {
104 message: String,
105 level: LogLevel,
106 time: SystemTime,
107}
108
109pub struct Log;
110
111static LOGS: RwLock<Vec<Logger>> = RwLock::new(Vec::new());
112static LOG_LEVEL: RwLock<LogLevel> = RwLock::new(LogLevel::Debug);
113
114fn get_level() -> LogLevel {
115 *LOG_LEVEL.read().unwrap()
116}
117
118fn level_priority(level: LogLevel) -> u8 {
119 match level {
120 LogLevel::Error => 1,
121 LogLevel::Warn => 2,
122 LogLevel::Info => 3,
123 LogLevel::Debug => 4,
124 }
125}
126
127impl Log {
128 pub fn set_up_logger(level: LogLevel) {
129 *LOG_LEVEL.write().unwrap() = level;
130 }
131
132 pub fn log_with_level(level: LogLevel, message: &str) {
133 if level_priority(level) <= level_priority(get_level()) {
134 let log = Logger {
135 message: message.to_string(),
136 level,
137 time: SystemTime::now(),
138 };
139
140 LOGS.write().unwrap().push(log);
141 }
142 }
143
144 pub fn log(message: &str) {
145 Self::log_with_level(get_level(), message);
146 }
147
148 pub fn log_info(message: &str) {
149 Self::log_with_level(LogLevel::Info, message);
150 }
151
152 pub fn log_debug(message: &str) {
153 Self::log_with_level(LogLevel::Debug, message);
154 }
155
156 pub fn log_error(message: &str) {
157 Self::log_with_level(LogLevel::Error, message);
158 }
159
160 pub fn log_warn(message: &str) {
161 Self::log_with_level(LogLevel::Warn, message);
162 }
163
164 pub fn get_logs() -> Vec<String> {
165 LOGS.read()
166 .unwrap()
167 .iter()
168 .map(|log| {
169 let since_unix = log
170 .time
171 .duration_since(UNIX_EPOCH)
172 .unwrap_or(Duration::from_secs(0));
173 format!(
174 "[{:?}] @ {}s → {}",
175 log.level,
176 since_unix.as_secs(),
177 log.message
178 )
179 })
180 .collect()
181 }
182
183 pub fn print_logs() {
184 for log in Self::get_logs() {
185 println!("{}", log);
186 }
187 }
188
189 pub fn clear() {
190 LOGS.write().unwrap().clear();
191 }
192}
193
194pub trait Loggable {
195 fn log(self);
196 fn log_info(self);
197 fn log_error(self);
198 fn log_debug(self);
199}
200
201impl Loggable for &str {
202 fn log(self) {
203 Log::log(self);
204 }
205 fn log_info(self) {
206 Log::log_info(self);
207 }
208 fn log_error(self) {
209 Log::log_error(self);
210 }
211 fn log_debug(self) {
212 Log::log_debug(self);
213 }
214}
215
216impl Loggable for String {
217 fn log(self) {
218 self.as_str().log()
219 }
220 fn log_info(self) {
221 self.as_str().log_info()
222 }
223 fn log_error(self) {
224 self.as_str().log_error()
225 }
226 fn log_debug(self) {
227 self.as_str().log_debug()
228 }
229}
230
231pub trait EqUtils<T: PartialEq> {
233 fn eq_to(&self, other: &T) -> bool;
235
236 fn not_eq_to(&self, other: &T) -> bool;
238}
239
240impl<T: PartialEq> EqUtils<T> for T {
241 fn eq_to(&self, other: &T) -> bool {
242 self == other
243 }
244 fn not_eq_to(&self, other: &T) -> bool {
245 self != other
246 }
247}
248
249pub trait OptionUtils<T> {
251 fn or_default_with(self, fallback: T) -> T;
253
254 #[must_use]
258 fn if_some<F: FnOnce(&T)>(self, f: F) -> Option<T>;
259}
260
261impl<T> OptionUtils<T> for Option<T> {
262 fn or_default_with(self, fallback: T) -> T {
263 self.unwrap_or(fallback)
264 }
265
266 fn if_some<F: FnOnce(&T)>(self, f: F) -> Option<T> {
267 if let Some(ref val) = self {
268 f(val);
269 }
270 self
271 }
272}
273
274pub trait StrUtils {
276 fn contains_all<'a, I>(&self, parts: I) -> bool
278 where
279 I: IntoIterator<Item = &'a str>;
280
281 fn contains_any<'a, I>(&self, parts: I) -> bool
283 where
284 I: IntoIterator<Item = &'a str>;
285
286 fn to_title_case(&self) -> String;
288}
289
290impl StrUtils for str {
291 fn contains_all<'a, I>(&self, parts: I) -> bool
292 where
293 I: IntoIterator<Item = &'a str>,
294 {
295 parts.into_iter().all(|part| self.contains(part))
296 }
297 fn contains_any<'a, I>(&self, parts: I) -> bool
298 where
299 I: IntoIterator<Item = &'a str>,
300 {
301 parts.into_iter().any(|part| self.contains(part))
302 }
303 fn to_title_case(&self) -> String {
304 let mut chars = self.chars();
305 match chars.next() {
306 None => String::new(),
307 Some(f) => f.to_uppercase().collect::<String>() + chars.as_str(),
308 }
309 }
310}
311
312pub trait MemUtils {
314 fn type_name(&self) -> &'static str;
316
317 fn mem_size(&self) -> usize;
319
320 fn view(&self);
322}
323
324impl<T> MemUtils for T {
325 fn type_name(&self) -> &'static str {
326 type_name::<T>()
327 }
328 fn mem_size(&self) -> usize {
329 std::mem::size_of::<T>()
330 }
331 fn view(&self) {
332 println!(
333 "[view] Type: {}, Size: {} bytes",
334 self.type_name(),
335 self.mem_size()
336 );
337 }
338}
339
340pub trait ConvertUtils: Sized {
341 fn to<T: TryFrom<Self>>(self) -> Option<T>;
342 fn to_or<T: TryFrom<Self>>(self, fallback: T) -> T;
343 fn to_result<T: TryFrom<Self>>(self) -> Result<T, T::Error>;
344}
345
346impl<T> ConvertUtils for T {
347 fn to<U: TryFrom<T>>(self) -> Option<U> {
348 U::try_from(self).ok()
349 }
350
351 fn to_or<U: TryFrom<T>>(self, fallback: U) -> U {
352 self.to().unwrap_or(fallback)
353 }
354
355 fn to_result<U: TryFrom<T>>(self) -> Result<U, U::Error> {
356 U::try_from(self)
357 }
358}
359
360pub trait BoolUtils {
361 #[must_use]
362 fn not(&self) -> bool;
363 fn then_val<T>(&self, val: T) -> Option<T>;
364 fn if_true<T, F: FnOnce() -> T>(&self, f: F) -> Option<T>;
365 fn if_false<T, F: FnOnce() -> T>(&self, f: F) -> Option<T>;
366 fn toggle(&mut self);
367}
368
369impl BoolUtils for bool {
370 fn not(&self) -> bool {
371 !self
372 }
373 fn then_val<T>(&self, val: T) -> Option<T> {
374 if *self { Some(val) } else { None }
375 }
376 fn if_true<T, F: FnOnce() -> T>(&self, f: F) -> Option<T> {
377 if *self { Some(f()) } else { None }
378 }
379 fn if_false<T, F: FnOnce() -> T>(&self, f: F) -> Option<T> {
380 if self.not() { Some(f()) } else { None }
381 }
382 fn toggle(&mut self) {
383 *self = !*self;
384 }
385}
386
387pub trait VecUtils<T> {
389 fn push_if(&mut self, push: T, cond: bool);
391
392 fn push_if_with<F: FnOnce() -> T>(&mut self, cond: bool, f: F);
394}
395
396impl<T> VecUtils<T> for Vec<T> {
397 fn push_if(&mut self, push: T, cond: bool) {
398 if cond {
399 self.push(push);
400 }
401 }
402 fn push_if_with<F: FnOnce() -> T>(&mut self, cond: bool, f: F) {
403 if cond {
404 self.push(f());
405 }
406 }
407}
408
409pub trait MapUtils<K, V> {
410 fn get_or<'a>(&'a self, key: &K, fallback: &'a V) -> &'a V;
411 fn insert_if(&mut self, key: K, value: V, cond: bool);
412}
413
414impl<K: Eq + Hash, V> MapUtils<K, V> for HashMap<K, V> {
415 fn get_or<'a>(&'a self, key: &K, fallback: &'a V) -> &'a V {
416 self.get(key).unwrap_or(fallback)
417 }
418
419 fn insert_if(&mut self, key: K, value: V, cond: bool) {
420 if cond {
421 self.insert(key, value);
422 }
423 }
424}
425
426pub trait ResultUtils<T, E> {
427 fn if_ok<F: FnOnce(&T)>(self, f: F) -> Self;
428 fn if_err<F: FnOnce(&E)>(self, f: F) -> Self;
429}
430
431impl<T, E: std::fmt::Debug> ResultUtils<T, E> for Result<T, E> {
432 fn if_ok<F: FnOnce(&T)>(self, f: F) -> Self {
433 if let Ok(ref val) = self {
434 f(val);
435 }
436 self
437 }
438
439 fn if_err<F: FnOnce(&E)>(self, f: F) -> Self {
440 if let Err(ref err) = self {
441 f(err);
442 }
443 self
444 }
445}
446
447pub trait DurationUtils {
449 fn pretty(&self) -> String;
451}
452
453impl DurationUtils for Duration {
454 fn pretty(&self) -> String {
455 let total_secs = self.as_secs();
456 let hours = total_secs / 3600;
457 let mins = (total_secs % 3600) / 60;
458 let secs = total_secs % 60;
459 format!("{}h {}m {}s", hours, mins, secs)
460 }
461}
462
463pub trait IteratorUtils: Iterator + Sized {
464 fn find_map_or<T, F: FnMut(Self::Item) -> Option<T>>(self, f: F, fallback: T) -> T;
465}
466
467impl<I: Iterator> IteratorUtils for I {
468 fn find_map_or<T, F: FnMut(Self::Item) -> Option<T>>(mut self, f: F, fallback: T) -> T {
469 self.find_map(f).unwrap_or(fallback)
470 }
471}
472
473pub trait IdentityUtils: Sized {
474 fn tap<F: FnOnce(&Self)>(self, f: F) -> Self;
475}
476
477impl<T> IdentityUtils for T {
478 fn tap<F: FnOnce(&Self)>(self, f: F) -> Self {
479 f(&self);
480 self
481 }
482}
483
484pub trait PanicUtils<T> {
486 fn unwrap_or_exit(self, msg: &str) -> T;
488}
489
490impl<T> PanicUtils<T> for Option<T> {
491 fn unwrap_or_exit(self, msg: &str) -> T {
492 self.unwrap_or_else(|| {
493 eprintln!("[FATAL]: {}", msg);
494 std::process::exit(1);
495 })
496 }
497}
498
499impl<T, U> PanicUtils<T> for Result<T, U> {
500 fn unwrap_or_exit(self, msg: &str) -> T {
501 self.unwrap_or_else(|_| {
502 eprintln!("[FATAL]: {}", msg);
503 std::process::exit(1);
504 })
505 }
506}
507
508pub trait ClampUtils {
509 fn clamp_to(self, min: Self, max: Self) -> Self;
510}
511impl ClampUtils for i32 {
512 fn clamp_to(self, min: Self, max: Self) -> Self {
513 self.max(min).min(max)
514 }
515}
516
517pub trait NumberUtils {
518 #[must_use]
519 fn is_even(&self) -> bool;
520 #[must_use]
521 fn is_odd(&self) -> bool;
522}
523
524impl NumberUtils for i32 {
525 fn is_even(&self) -> bool {
526 self % 2 == 0
527 }
528 fn is_odd(&self) -> bool {
529 self % 2 != 0
530 }
531}