1#![cfg_attr(not(feature = "std"), no_std)]
2
3use core::fmt::{Debug, Display};
4
5#[cfg(feature = "ufmt")]
6use core::time::Duration;
7
8#[cfg(feature = "ufmt")]
9use ufmt::{uDebug, uWrite};
10
11macro_rules! define_colors {
12 ($($name:ident => $color:expr),* $(,)?) => {
13 $(
14 #[cfg(feature = "colors")]
15 const $name: &str = $color;
16 #[cfg(not(feature = "colors"))]
17 const $name: &str = "";
18 )*
19 };
20}
21
22define_colors! {
23 RESET => "\x1b[0m",
24 LIGHT_GREEN => "\x1b[92m",
25 LIGHT_BLUE => "\x1b[94m",
26 LIGHT_RED => "\x1b[91m",
27 LIGHT_YELLOW => "\x1b[93m",
28 RED => "\x1b[31m",
29}
30
31#[derive(Clone, Copy, PartialEq, Eq)]
32pub enum StatusLevel {
33 Ok = 0,
34 Info = 1,
35 Error = 2,
36 Warning = 3,
37 Critical = 4,
38}
39
40impl StatusLevel {
41 fn to_color(&self) -> &'static str {
42 match self {
43 StatusLevel::Ok => LIGHT_GREEN,
44 StatusLevel::Info => LIGHT_BLUE,
45 StatusLevel::Error => LIGHT_RED,
46 StatusLevel::Warning => LIGHT_YELLOW,
47 StatusLevel::Critical => RED,
48 }
49 }
50}
51
52macro_rules! impl_status_format {
53 (
54 $self:expr,$formatter:ident, $write_macro:ident,
55 $($variant:ident => $symbol:expr, $color:expr),* $(,)?
56 ) => {
57 match $self {
58 $(
59 StatusLevel::$variant => $write_macro!($formatter, "{}{}&:{}", $color, $symbol, RESET)?,
60 )*
61 }
62 };
63}
64
65#[cfg(feature = "ufmt")]
66impl uDebug for StatusLevel {
67 fn fmt<W>(&self, f: &mut ufmt::Formatter<'_, W>) -> Result<(), W::Error>
68 where
69 W: ufmt::uWrite + ?Sized,
70 {
71 use ufmt::uwrite;
72
73 impl_status_format!(self,f, uwrite,
74 Ok => "O", LIGHT_GREEN,
75 Info => "I", LIGHT_BLUE,
76 Error => "E", LIGHT_RED,
77 Warning => "W", LIGHT_YELLOW,
78 Critical => "C", RED,
79 );
80
81 Ok(())
82 }
83}
84
85impl Debug for StatusLevel {
86 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
87 impl_status_format!(self,f, write,
88 Ok => "O", LIGHT_GREEN,
89 Info => "I", LIGHT_BLUE,
90 Error => "E", LIGHT_RED,
91 Warning => "W", LIGHT_YELLOW,
92 Critical => "C", RED,
93 );
94 Ok(())
95 }
96}
97
98#[cfg(feature = "alloc")]
99extern crate alloc;
100#[cfg(feature = "alloc")]
101use alloc::boxed::Box;
102
103#[cfg(feature = "ufmt")]
104pub trait UStorageProvider {
105 fn write_data(&mut self, d: impl uDebug);
106}
107
108use core::fmt::Arguments;
109
110pub trait StorageProvider {
111 fn write_data(&mut self, args: Arguments, debuglevel: &StatusLevel);
113}
114
115#[cfg(feature = "std")]
116impl StorageProvider for () {
117 fn write_data(&mut self, args: Arguments<'_>, _debuglevel: &StatusLevel) {
118 print!("{args}")
119 }
120}
121
122pub trait TimeProvider {
123 fn now() -> Self;
124 fn elapsed(&self) -> core::time::Duration;
125 fn write(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result;
126}
127
128#[cfg(feature = "std")]
129use std::time::Instant;
130
131#[cfg(feature = "std")]
132impl TimeProvider for Instant {
133 fn now() -> Self {
134 Instant::now()
135 }
136 fn elapsed(&self) -> core::time::Duration {
137 self.elapsed()
138 }
139 fn write(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
140 write!(f, "{:?}:", self.elapsed())?;
141 Ok(())
142 }
143}
144
145impl TimeProvider for () {
146 fn now() -> Self {}
147 fn elapsed(&self) -> core::time::Duration {
148 core::time::Duration::ZERO
149 }
150 fn write(&self, _f: &mut core::fmt::Formatter) -> core::fmt::Result {
151 Ok(())
152 }
153}
154
155macro_rules! impl_log_methods {
156 ($($method:ident => $level:expr),* $(,)?) => {
157 $(
158 pub fn $method(&mut self, args: impl Display) {
159 self.logdisp($level, args);
160 }
161 )*
162 };
163}
164
165macro_rules! impl_try_get {
166 ($error_bound:path, owned) => {
167 #[cfg(feature = "std")]
168 #[cfg(feature = "ufmt")]
169 pub fn try_get<O>(
170 mut self, tryresult: Result<O, E: Debug>,
172 redirectfn: fn(Self) -> (),
173 ) -> (O, Self) {
174 match tryresult {
175 Ok(x) => (x, self),
176 Err(err) => {
177 self.log(StatusLevel::Warning, UDebugStr(&err.to_string()));
178 redirectfn(self);
179 std::process::exit(1);
180 }
181 }
182 }
183
184 #[cfg(feature = "std")]
185 #[cfg(not(feature = "ufmt"))]
186 pub fn try_get<O>(
187 mut self, tryresult: Result<O, Box<dyn core::error::Error>>,
189 redirectfn: fn(Self) -> (),
190 ) -> (O, Self) {
191 match tryresult {
192 Ok(x) => (x, self),
193 Err(err) => {
194 self.log(StatusLevel::Warning, err);
195 redirectfn(self);
196 std::process::exit(1);
197 }
198 }
199 }
200
201 #[cfg(not(feature = "std"))]
202 pub fn try_get<O, E: $error_bound>(
203 mut self, tryresult: Result<O, E>,
205 redirectfn: fn(Self) -> (),
206 ) -> (O, Self) {
207 match tryresult {
208 Ok(x) => (x, self),
209 Err(err) => {
210 self.log(StatusLevel::Warning, err);
211 redirectfn(self);
212 loop {}
213 }
214 }
215 }
216 };
217
218 ($error_bound:path, cloned) => {
219 #[cfg(feature = "std")]
220 #[cfg(not(feature = "ufmt"))]
221 pub fn try_get<O>(
222 &mut self, tryresult: Result<O, Box<dyn core::error::Error>>,
224 redirectfn: fn(Self) -> (),
225 ) -> (O, Self) {
226 let mut new_self = self.clone();
227 match tryresult {
228 Ok(x) => (x, new_self),
229 Err(err) => {
230 new_self.log(StatusLevel::Warning, err);
231 redirectfn(new_self);
232 std::process::exit(1);
233 }
234 }
235 }
236 #[cfg(feature = "std")]
237 #[cfg(feature = "ufmt")]
238 pub fn try_get<O>(
239 &mut self, tryresult: Result<O, Box<dyn core::error::Error>>,
241 redirectfn: fn(Self) -> (),
242 ) -> (O, Self) {
243 let mut new_self = self.clone();
244 match tryresult {
245 Ok(x) => (x, new_self),
246 Err(err) => {
247 new_self.log(StatusLevel::Warning, UDebugStr(&err.to_string()));
248 redirectfn(new_self);
249 std::process::exit(1);
250 }
251 }
252 }
253
254 #[cfg(not(feature = "std"))]
255 pub fn try_get<O, E: $error_bound>(
256 &mut self, tryresult: Result<O, E>,
258 redirectfn: fn(Self) -> (),
259 ) -> (O, Self) {
260 let mut new_self = self.clone();
261 match tryresult {
262 Ok(x) => (x, new_self),
263 Err(err) => {
264 new_self.log(StatusLevel::Warning, err);
265 redirectfn(new_self);
266 loop {}
267 }
268 }
269 }
270 };
271}
272
273#[derive(Clone)]
274pub struct MultiLogger<T: TimeProvider + Clone, S: StorageProvider + Clone>(pub T, pub S);
275
276pub struct Logger<T: TimeProvider, S: StorageProvider>(pub T, pub S);
277
278impl<'a, T: TimeProvider + Clone, S: StorageProvider + Clone> MultiLogger<T, S>
279where
280 Self: Clone,
281{
282 pub fn log(&mut self, level: StatusLevel, args: impl Debug) {
283 self.1.write_data(
284 format_args!(
285 "{:?}{} {}{:?}{}\n",
286 level,
287 TimeFormatter(&self.0),
288 level.to_color(),
289 args,
290 RESET
291 ),
292 &level,
293 );
294 }
295
296 pub fn logdisp(&mut self, level: StatusLevel, args: impl Display) {
297 self.1.write_data(
298 format_args!(
299 "{:?}{} {}{}{}\n",
300 level,
301 TimeFormatter(&self.0),
302 level.to_color(),
303 args,
304 RESET
305 ),
306 &level,
307 );
308 }
309
310 impl_log_methods! {
311 log_err => StatusLevel::Error,
312 log_ok => StatusLevel::Ok,
313 log_warn => StatusLevel::Warning,
314 log_info => StatusLevel::Info,
315 }
316
317 #[cfg(feature = "alloc")]
318 pub fn try_run<O>(&mut self, tryresult: Result<O, Box<dyn core::error::Error>>) {
319 if let Err(err) = tryresult {
320 self.log(StatusLevel::Error, err);
321 }
322 }
323 #[cfg(feature = "alloc")]
324 pub fn try_run_get<O, F>(&mut self, tryresult: Result<O, Box<dyn core::error::Error>>, value: F)
325 where
326 F: FnOnce(O),
327 {
328 match tryresult {
329 Ok(ok) => value(ok),
330 Err(err) => self.log(StatusLevel::Error, err),
331 }
332 }
333
334 #[cfg(not(feature = "alloc"))]
335 pub fn try_run<O, E: core::fmt::Debug>(&mut self, tryresult: Result<O, E>) {
336 if let Err(err) = tryresult {
337 self.log(StatusLevel::Error, err);
338 }
339 }
340 #[cfg(not(feature = "alloc"))]
341 pub fn try_run_get<O, E: core::fmt::Debug, F>(&mut self, tryresult: Result<O, E>, value: F)
342 where
343 F: FnOnce(O),
344 {
345 match tryresult {
346 Ok(ok) => value(ok),
347 Err(err) => self.log(StatusLevel::Error, err),
348 }
349 }
350
351 impl_try_get!(core::fmt::Debug, cloned);
352}
353
354impl<'a, T: TimeProvider, S: StorageProvider> Logger<T, S> {
355 pub fn log(&mut self, level: StatusLevel, args: impl Debug) {
356 self.1.write_data(
357 format_args!(
358 "{:?}{} {}{:?}{}\n",
359 level,
360 TimeFormatter(&self.0),
361 level.to_color(),
362 args,
363 RESET
364 ),
365 &level,
366 );
367 }
368
369 pub fn logdisp(&mut self, level: StatusLevel, args: impl Display) {
370 self.1.write_data(
371 format_args!(
372 "{:?}{} {}{}{}\n",
373 level,
374 TimeFormatter(&self.0),
375 level.to_color(),
376 args,
377 RESET
378 ),
379 &level,
380 );
381 }
382
383 impl_log_methods! {
384 log_err => StatusLevel::Error,
385 log_ok => StatusLevel::Ok,
386 log_warn => StatusLevel::Warning,
387 log_info => StatusLevel::Info,
388 }
389
390 #[cfg(feature = "alloc")]
391 pub fn try_run<O>(&mut self, tryresult: Result<O, Box<dyn core::error::Error>>) {
392 if let Err(err) = tryresult {
393 self.log(StatusLevel::Error, err);
394 }
395 }
396 #[cfg(feature = "alloc")]
397 pub fn try_run_get<O, F>(&mut self, tryresult: Result<O, Box<dyn core::error::Error>>, value: F)
398 where
399 F: FnOnce(O),
400 {
401 match tryresult {
402 Ok(ok) => value(ok),
403 Err(err) => self.log(StatusLevel::Error, err),
404 }
405 }
406
407 #[cfg(not(feature = "alloc"))]
408 pub fn try_run<O, E: core::fmt::Debug>(&mut self, tryresult: Result<O, E>) {
409 if let Err(err) = tryresult {
410 self.log(StatusLevel::Error, err);
411 }
412 }
413 #[cfg(not(feature = "alloc"))]
414 pub fn try_run_get<O, E: core::fmt::Debug, F>(&mut self, tryresult: Result<O, E>, value: F)
415 where
416 F: FnOnce(O),
417 {
418 match tryresult {
419 Ok(ok) => value(ok),
420 Err(err) => self.log(StatusLevel::Error, err),
421 }
422 }
423
424 impl_try_get!(core::fmt::Debug, owned);
425}
426
427struct TimeFormatter<'a, T: TimeProvider>(&'a T);
428
429impl<'a, T: TimeProvider> core::fmt::Display for TimeFormatter<'a, T> {
430 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
431 self.0.write(f)
432 }
433}
434
435#[cfg(feature = "ufmt")]
436pub struct UDebugStr<'a>(pub &'a str);
437
438#[cfg(feature = "ufmt")]
439impl<'a> Debug for UDebugStr<'a> {
440 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
441 write!(f, "{}", self.0)?;
442 Ok(())
443 }
444}
445
446#[cfg(feature = "ufmt")]
447impl<'a> uDebug for UDebugStr<'a> {
448 fn fmt<W>(&self, f: &mut ufmt::Formatter<'_, W>) -> Result<(), W::Error>
449 where
450 W: uWrite + ?Sized,
451 {
452 f.write_str(self.0)?;
453 Ok(())
454 }
455}
456
457#[cfg(feature = "ufmt")]
458struct UDebugDuration(Duration);
459
460#[cfg(feature = "ufmt")]
461impl uDebug for UDebugDuration {
462 fn fmt<W>(&self, f: &mut ufmt::Formatter<'_, W>) -> Result<(), W::Error>
463 where
464 W: uWrite + ?Sized,
465 {
466 use ufmt::uwrite;
467
468 let nanos = self.0.as_nanos();
469 let micros = self.0.as_micros();
470 let millis = self.0.as_millis();
471 let secs = self.0.as_secs();
472
473 if nanos < 1_000 {
474 uwrite!(f, "{}ns", nanos as u32)
475 } else if micros < 1_000 {
476 uwrite!(f, "{}μs", micros as u32)
477 } else if millis < 1_000 {
478 uwrite!(f, "{}ms", millis as u32)
479 } else if secs < 60 {
480 let remaining_millis = (millis % 1000) as u32;
481 uwrite!(f, "{}.{}s", secs, remaining_millis)
482 } else if secs < 3600 {
483 let mins = secs / 60;
484 let remaining_secs = secs % 60;
485 uwrite!(f, "{}:{}min", mins, remaining_secs)
486 } else if secs < 86400 {
487 let hours = secs / 3600;
488 let mins = (secs % 3600) / 60;
489 let remaining_secs = secs % 60;
490 uwrite!(f, "{}:{}:{}", hours, mins, remaining_secs)
491 } else {
492 let days = secs / 86400;
493 let hours = (secs % 86400) / 3600;
494 if hours > 0 {
495 uwrite!(f, "{}d{}h", days, hours)
496 } else {
497 uwrite!(f, "{}d", days)
498 }
499 }
500 }
501}
502
503#[cfg(feature = "ufmt")]
504macro_rules! impl_log_methods_ufmt {
505 ($($method:ident => $level:expr),* $(,)?) => {
506 $(
507 pub fn $method(&mut self, args: &str) {
508 self.logdisp($level, args);
509 }
510 )*
511 };
512}
513
514#[cfg(feature = "std")]
515#[cfg(feature = "ufmt")]
516struct StdWriter<'a>(&'a mut dyn std::io::Write);
517
518#[cfg(feature = "std")]
519#[cfg(feature = "ufmt")]
520impl<'a> uWrite for StdWriter<'a> {
521 type Error = ();
522 fn write_str(&mut self, s: &str) -> Result<(), ()> {
523 self.0.write_all(s.as_bytes()).map_err(|_| ())
524 }
525}
526
527#[cfg(feature = "std")]
528#[cfg(feature = "ufmt")]
529impl UStorageProvider for () {
530 fn write_data(&mut self, d: impl uDebug) {
531 use std::io::{self};
532 let mut stdout = io::stdout();
533 let mut writer = StdWriter(&mut stdout);
534 let _ = d.fmt(&mut ufmt::Formatter::new(&mut writer));
535 }
536}
537
538#[cfg(feature = "ufmt")]
539pub struct ULogger<T: TimeProvider, S: UStorageProvider>(pub T, pub S);
540
541#[cfg(feature = "ufmt")]
542#[derive(Clone)]
543pub struct MultiULogger<T: TimeProvider + Clone, S: UStorageProvider + Clone>(pub T, pub S);
544
545#[cfg(feature = "ufmt")]
546impl<T: TimeProvider, S: UStorageProvider> ULogger<T, S> {
547 pub fn log(&mut self, level: StatusLevel, args: impl uDebug) {
548 let timestamp = self.0.elapsed();
549 self.1.write_data(level);
550 self.1.write_data(UDebugDuration(timestamp));
551 self.1.write_data(UDebugStr(level.to_color()));
552 self.1.write_data(args);
553 self.1.write_data(UDebugStr(RESET));
554 self.1.write_data(UDebugStr("\n"));
555 }
556
557 pub fn logdisp(&mut self, level: StatusLevel, args: &str) {
558 let timestamp = self.0.elapsed();
559 self.1.write_data(level);
560 self.1.write_data(UDebugDuration(timestamp));
561 self.1.write_data(UDebugStr(level.to_color()));
562 self.1.write_data(UDebugStr(args));
563 self.1.write_data(UDebugStr(RESET));
564 self.1.write_data(UDebugStr("\n"));
565 }
566
567 impl_log_methods_ufmt! {
568 log_err => StatusLevel::Error,
569 log_ok => StatusLevel::Ok,
570 log_warn => StatusLevel::Warning,
571 log_info => StatusLevel::Info,
572 }
573
574 #[cfg(feature = "alloc")]
575 #[cfg(not(feature = "ufmt"))]
576 pub fn try_run<O>(&mut self, tryresult: Result<O, Box<dyn core::error::Error>>) {
577 if let Err(err) = tryresult {
578 self.log(StatusLevel::Error, err);
579 }
580 }
581
582 #[cfg(feature = "alloc")]
583 #[cfg(not(feature = "ufmt"))]
584 pub fn try_run_get<O, F>(&mut self, tryresult: Result<O, Box<dyn core::error::Error>>, value: F)
585 where
586 F: FnOnce(O),
587 {
588 match tryresult {
589 Ok(ok) => value(ok),
590 Err(err) => self.log(StatusLevel::Error, err),
591 }
592 }
593
594 #[cfg(feature = "alloc")]
595 #[cfg(feature = "ufmt")]
596 pub fn try_run<O>(&mut self, tryresult: Result<O, Box<dyn core::error::Error>>) {
597 if let Err(err) = tryresult {
598 self.log(StatusLevel::Error, UDebugStr(&err.to_string()));
599 }
600 }
601
602 #[cfg(feature = "alloc")]
603 #[cfg(feature = "ufmt")]
604 pub fn try_run_get<O, F>(&mut self, tryresult: Result<O, Box<dyn core::error::Error>>, value: F)
605 where
606 F: FnOnce(O),
607 {
608 if let Err(err) = tryresult {
609 match tryresult {
610 Ok(ok) => value(ok),
611 Err(err) => self.log(StatusLevel::Error, UDebugStr(&err.to_string())),
612 }
613 }
614 }
615
616 #[cfg(not(feature = "alloc"))]
617 pub fn try_run<O, E: ufmt::uDebug>(&mut self, tryresult: Result<O, E>) {
618 if let Err(err) = tryresult {
619 self.log(StatusLevel::Error, err);
620 }
621 }
622 #[cfg(not(feature = "alloc"))]
623 pub fn try_run_get<O, E: ufmt::uDebug, F>(&mut self, tryresult: Result<O, E>, value: F)
624 where
625 F: FnOnce(O),
626 {
627 if let Err(err) = tryresult {
628 match tryresult {
629 Ok(ok) => value(ok),
630 Err(err) => self.log(StatusLevel::Error, err),
631 }
632 }
633 }
634
635 impl_try_get!(ufmt::uDebug, owned);
636}
637
638#[cfg(feature = "ufmt")]
639impl<T: TimeProvider + Clone, S: UStorageProvider + Clone> MultiULogger<T, S>
640where
641 Self: Clone,
642{
643 pub fn log(&mut self, level: StatusLevel, args: impl uDebug) {
644 let timestamp = self.0.elapsed();
645 self.1.write_data(level);
646 self.1.write_data(UDebugDuration(timestamp));
647 self.1.write_data(UDebugStr(level.to_color()));
648 self.1.write_data(args);
649 self.1.write_data(UDebugStr(RESET));
650 self.1.write_data(UDebugStr("\n"));
651 }
652
653 pub fn logdisp(&mut self, level: StatusLevel, args: &str) {
654 let timestamp = self.0.elapsed();
655 self.1.write_data(level);
656 self.1.write_data(UDebugDuration(timestamp));
657 self.1.write_data(UDebugStr(level.to_color()));
658 self.1.write_data(UDebugStr(args));
659 self.1.write_data(UDebugStr(RESET));
660 self.1.write_data(UDebugStr("\n"));
661 }
662
663 impl_log_methods_ufmt! {
664 log_err => StatusLevel::Error,
665 log_ok => StatusLevel::Ok,
666 log_warn => StatusLevel::Warning,
667 log_info => StatusLevel::Info,
668 }
669
670 #[cfg(feature = "alloc")]
671 pub fn try_run<O>(&mut self, tryresult: Result<O, Box<dyn core::error::Error>>) {
672 if let Err(err) = tryresult {
673 self.log(StatusLevel::Error, UDebugStr(&err.to_string()));
674 }
675 }
676
677 #[cfg(feature = "alloc")]
678 pub fn try_run_get<O>(
679 &mut self,
680 tryresult: Result<O, Box<dyn core::error::Error>>,
681 value: fn(O) -> (),
682 ) {
683 match tryresult {
684 Ok(ok) => value(o),
685 Err(err) => self.log(StatusLevel::Error, UDebugStr(&err.to_string())),
686 }
687 }
688
689 impl_try_get!(ufmt::uDebug, cloned);
690}
691
692#[cfg(feature = "std")]
693#[macro_export]
694macro_rules! black_box_cand {
695 () => {
696 ::std::panic::set_hook(Box::new(|info| {
697 let mut logger = $crate::Logger(::std::time::Instant::now(), ());
698 let payload = if let Some(s) = info.payload().downcast_ref::<&'static str>() {
699 *s
700 } else if let Some(s) = info.payload().downcast_ref::<String>() {
701 s.as_str()
702 } else {
703 "unknown panic payload"
704 };
705 let (before, after) = if let Some(pos) = payload.find(": ") {
706 (&payload[0..pos + 2], &payload[pos + 2..])
707 } else {
708 ("", payload)
709 };
710 let message = if let Some(location) = info.location() {
711 format!(
712 "\x1b[0mpanicked at {}:{}:{}:\n\x1b[0m{}\x1b[31m{}\x1b[0m",
713 location.file(),
714 location.line(),
715 location.column(),
716 before,
717 after
718 )
719 } else {
720 format!(
721 "\x1b[0mpanicked at unknown location:\n\x1b[0m{}\x1b[31m{}\x1b[0m",
722 before, after
723 )
724 };
725 logger.logdisp($crate::StatusLevel::Critical, &message);
726 }));
727 };
728
729 ($logger_expr:expr) => {
730 ::std::panic::set_hook(Box::new(|info| {
731 let mut logger = $logger_expr;
732 let payload = if let Some(s) = info.payload().downcast_ref::<&'static str>() {
733 *s
734 } else if let Some(s) = info.payload().downcast_ref::<String>() {
735 s.as_str()
736 } else {
737 "unknown panic payload"
738 };
739 let (before, after) = if let Some(pos) = payload.find(": ") {
740 (&payload[0..pos + 2], &payload[pos + 2..])
741 } else {
742 ("", payload)
743 };
744 let message = if let Some(location) = info.location() {
745 format!(
746 "\x1b[0mpanicked at {}:{}:{}:\n\x1b[0m{}\x1b[31m{}\x1b[0m",
747 location.file(),
748 location.line(),
749 location.column(),
750 before,
751 after
752 )
753 } else {
754 format!(
755 "\x1b[0mpanicked at unknown location:\n\x1b[0m{}\x1b[31m{}\x1b[0m",
756 before, after
757 )
758 };
759 logger.logdisp($crate::StatusLevel::Critical, &message);
760 }))
761 };
762}
763
764#[cfg(feature = "std")]
765#[macro_export]
766macro_rules! black_box_cand_global {
767 ($logger:expr) => {
768 let mut logger = $logger;
769 ::std::panic::set_hook(Box::new(move |info| {
770 let payload = if let Some(s) = info.payload().downcast_ref::<&'static str>() {
771 *s
772 } else if let Some(s) = info.payload().downcast_ref::<String>() {
773 s.as_str()
774 } else {
775 "unknown panic payload"
776 };
777 let (before, after) = if let Some(pos) = payload.find(": ") {
778 (&payload[0..pos + 2], &payload[pos + 2..])
779 } else {
780 ("", payload)
781 };
782 let message = if let Some(location) = info.location() {
783 format!(
784 "\x1b[0mpanicked at {}:{}:{}:\n\x1b[0m{}\x1b[31m{}\x1b[0m",
785 location.file(),
786 location.line(),
787 location.column(),
788 before,
789 after
790 )
791 } else {
792 format!(
793 "\x1b[0mpanicked at unknown location:\n\x1b[0m{}\x1b[31m{}\x1b[0m",
794 before, after
795 )
796 };
797 if let Ok(mut guard) = logger.lock() {
798 guard.logdisp(cand::StatusLevel::Critical, &message);
799 }
800 }));
801 };
802}