cand/
lib.rs

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    /// Write log data directly - single responsibility
112    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, // Takes ownership
171            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, // Takes ownership
188            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, // Takes ownership
204            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, // Takes reference
223            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, // Takes reference
240            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, // Takes reference
257            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}