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
pub use super::result::{UniformUnwrap, UniformUnwrapOrDefault};
use super::{derives::*, ext::*};
use crate::{asyn::*, sync};

pub async fn Term() -> Unblock<sync::io::Stdout> {
	Unblock::new(sync::io::stdout())
}

pub async fn TermErr() -> Unblock<sync::io::Stderr> {
	Unblock::new(sync::io::stderr())
}

pub async fn File() -> fs::File {
	fs::File::create("log.txt").await.expect("E| Couldn't create log file")
}

pub async fn Null() -> Unblock<io::Sink> {
	Unblock::new(io::sink())
}

pub struct Logger;
impl Logger {
	pub fn new<T, F>(out: impl FnOnce() -> F + SendStat, l: Level) -> Self
	where
		T: AsyncWrite + Unpin + Send,
		F: Future<Output = T> + Send,
	{
		Self::init_logger(out, l);
		Self
	}
	#[inline(always)]
	pub fn log(l: Level, msg: String) {
		if (l as i32) <= Self::level() {
			Self::init_logger(check_order, Level::INFO);

			unsafe { LOGGER.get() }
				.expect("E| Logger already exited")
				.sender
				.send(Message::M(msg))
				.expect("E| Failed to send log");
		}
	}
	pub fn shutdown_hook(f: impl FnOnce() + SendStat) {
		POSTMORTEM.lock().unwrap().push(Box(f));
	}

	pub fn level() -> i32 {
		unsafe { LEVEL as i32 }
	}
	pub fn set_level(l: Level) {
		unsafe { LEVEL = l }
	}
	fn init_logger<T, F>(out: impl FnOnce() -> F + SendStat, l: Level)
	where
		T: AsyncWrite + Unpin + Send,
		F: Future<Output = T> + Send,
	{
		unsafe {
			LOGGER.get_or_init(move || {
				LEVEL = l;
				let (sender, reciever) = chan::unbounded::<Message>();
				let handle = task::spawn(async move {
					let mut out = out().await;
					while let Ok(msg) = reciever.recv_async().await {
						if let Message::M(msg) = msg {
							out.write_all(msg.as_bytes()).await.expect("E| Failed log write");
						} else {
							out.flush().await.expect("E| Failed log flush");
							break;
						}
					}
				});
				LoggerState { handle, sender }
			});
		}
	}
}
impl Drop for Logger {
	fn drop(&mut self) {
		Self::init_logger(check_order, Level::INFO);
		POSTMORTEM.lock().unwrap().drain(..).for_each(|f| f());
		let LoggerState { handle, sender } = unsafe { LOGGER.take() }.unwrap();
		sender.send(Message::Close).expect("E| Failed to close log");
		task::block_on(handle);
	}
}

#[derive(Clone, Copy)]
pub enum Level {
	PRINT = 0,
	WARNING = 1,
	INFO = 2,
	DEBUG = 3,
}
static mut LEVEL: Level = Level::INFO;
static mut LOGGER: OnceLock<LoggerState> = OnceLock::new();
static POSTMORTEM: sync::Mutex<Vec<Box<dyn FnOnce() + Send + 'static>>> = sync::Mutex::new(vec![]);

struct LoggerState {
	handle: Task<()>,
	sender: Sender<Message>,
}

enum Message {
	M(String),
	Close,
}

async fn check_order() -> Unblock<sync::io::Stdout> {
	panic!("E| No logger! Add 'LOGGER!(logging::Term, INFO);' as first line in main()");
}