1use std::sync::{Arc, Mutex, Weak};
2use colored::{self, Color, Colorize};
3
4lazy_static::lazy_static! {
5 static ref BUS: Mutex<Option<Arc<EventBus<LogEvent>>>> = Mutex::new(None);
6}
7
8#[cfg(feature = "middleware")]
9pub mod middleware;
10
11#[derive(Clone)]
12pub struct EventBus<E> {
13 sender: crossbeam::channel::Sender<E>,
14 receiver: crossbeam::channel::Receiver<E>,
15}
16
17impl<E> EventBus<E> {
18 pub fn new(cap: usize) -> Self {
19 let (sender, receiver) = crossbeam::channel::bounded(cap);
20 Self { sender, receiver }
21 }
22
23 pub fn push(&self, event: E) {
24 let _ = self.sender.send(event);
25 }
26}
27
28pub fn init(cap: usize, formatter: fn(content: &str, level: &str, color: Color, strong: bool) -> String) -> std::thread::JoinHandle<()> {
54 let bus = Arc::new(EventBus::<LogEvent>::new(cap));
55 let receiver = bus.receiver.clone();
56 BUS.lock().unwrap().replace(bus);
57
58 std::thread::spawn(move || {
59 loop {
60 match receiver.recv() {
61 Ok(event) => {
62 let event = formatter(
63 &event.content,
64 &event.meta.string,
65 event.meta.color,
66 event.strong
67 );
68 println!("{}", event);
69 }
70 Err(_) => {
71 break;
72 }
73 }
74 }
75 })
76}
77
78
79#[cfg(feature = "async")]
80pub fn async_init(cap: usize, formatter: fn(content: &str, level: &str, color: Color, strong: bool) -> String) -> tokio::task::JoinHandle<()> {
106
107
108 let bus = Arc::new(EventBus::<LogEvent>::new(cap));
109 let receiver = bus.receiver.clone();
110 BUS.lock().unwrap().replace(bus);
111
112 tokio::spawn(async move {
113 loop {
114 match tokio::task::spawn_blocking({
115 let receiver = receiver.clone();
116 move || receiver.recv()
117 }).await {
118 Ok(Ok(event)) => {
119 let event = formatter(
120 &event.content,
121 &event.meta.string,
122 event.meta.color,
123 event.strong
124 );
125 println!("{}", event);
126 }
127 Ok(Err(_)) => {
128 break;
129 }
130 Err(_) => {
131 break;
132 }
133 }
134 }
135 })
136}
137
138pub fn close() {
139 *BUS.lock().unwrap() = None;
140}
141
142
143pub fn default_formatter(content: &str, level: &str, color: Color, strong: bool) -> String {
149
150 let mut result = String::new();
151 let timestamp = chrono::Local::now().format("%H:%M:%S").to_string()
152 .color(Color::BrightBlack);
153 let lines: Vec<&str> = content.lines().collect();
154
155 for (i, line) in lines.iter().enumerate() {
156 if i == 0 {
157 result.push_str(&format!(
158 "{} [{}] : {}",
159 timestamp,
160 level.color(color).bold(),
161 if strong { line.color(color).bold().to_string() }
162 else { line.to_string() }
163 ));
164 } else if i < lines.len() - 1 {
165 result.push_str(&format!(
166 "\n {} {}",
167 ":".color(Color::BrightBlack),
168 if strong { line.color(color).bold().to_string() }
169 else { line.to_string() }
170 ));
171 } else {
172 result.push_str(&format!(
173 "\n : {}",
174 if strong { line.color(color).bold().to_string() }
175 else { line.to_string() }
176 ));
177 }
178 }
179 result
180}
181
182pub struct LogMeta {
183 string: String,
184 color: Color
185}
186
187pub struct LogEvent {
188 meta: LogMeta,
189 content: String,
190 strong: bool
191}
192
193#[derive(Clone)]
194pub struct Logger {
195 bus: Weak<EventBus<LogEvent>>,
196 temporary_strong: bool
197}
198
199impl Logger {
200 pub fn log(&mut self, meta: LogMeta, content: impl std::fmt::Display) {
201 let content = format!("{}", content);
202 if let Some(bus) = self.bus.upgrade() {
203 let event = LogEvent {
204 meta,
205 content,
206 strong: self.temporary_strong
207 };
208 bus.push(event);
209 }
210 self.temporary_strong = false;
211 }
212
213 pub fn info(&mut self, content: impl std::fmt::Display) {
214 self.log(
215 LogMeta {
216 string: "I".to_string(),
217 color: Color::Blue
218 },
219 content
220 );
221 }
222
223 pub fn warn(&mut self, content: impl std::fmt::Display) {
224 self.log(
225 LogMeta {
226 string: "W".to_string(),
227 color: Color::Yellow
228 },
229 content
230 );
231 }
232
233 pub fn error(&mut self, content: impl std::fmt::Display) {
234 self.log(
235 LogMeta {
236 string: "E".to_string(),
237 color: Color::Red
238 },
239 content
240 );
241 }
242
243 pub fn debug(&mut self, content: impl std::fmt::Display) {
244 self.log(
245 LogMeta {
246 string: "D".to_string(),
247 color: Color::Magenta
248 },
249 content
250 );
251 }
252
253 pub fn success(&mut self, content: impl std::fmt::Display) {
254 self.log(
255 LogMeta {
256 string: "S".to_string(),
257 color: Color::Green
258 },
259 content
260 );
261 }
262
263 pub fn strong(&mut self) -> &mut Self {
264 self.temporary_strong = true;
265 self
266 }
267}
268
269pub fn get_logger() -> Logger {
271 Logger {
272 bus: Arc::downgrade(
273 BUS.lock().unwrap()
274 .as_ref()
275 .expect("Logger not initialized")
276 ),
277 temporary_strong: false
278 }
279}