debug_et_diagnostics/
macros.rs

1/// colofully prints the "location" of the macro call (function name, filename and line number) in the code
2#[macro_export]
3macro_rules! location {
4    () => {{
5        let location = format!(
6            "{}{}{}:{}",
7            $crate::color::auto($crate::function_name!()),
8            $crate::color::fore(" @ ", 220),
9            $crate::filename!(),
10            $crate::color::auto(line!().to_string())
11        );
12        location
13    }};
14    (begin) => {
15        $crate::tag!([
16            $crate::color::auto(format!("in function")),
17            $crate::location!()
18        ]
19        .join(" "))
20    };
21    (end) => {
22        $crate::tag!([
23            $crate::color::auto(format!("from function")),
24            $crate::location!()
25        ]
26        .join(" "))
27    };
28    (unexpected) => {
29        [
30            $crate::color::fore(format!("<unexpected branch in function"), 160),
31            $crate::location!(),
32            $crate::color::fore(format!(">"), 160),
33        ]
34        .join(" ")
35    };
36}
37/// colofully prints the filename of the macro call
38#[macro_export]
39macro_rules! filename {
40    () => {{
41        let mut parts = file!()
42            .split(std::path::MAIN_SEPARATOR_STR)
43            .map(String::from)
44            .map(|part| $crate::color::auto(part.to_string()))
45            .collect::<Vec<String>>();
46        let (folder, filename) = if parts.len() > 1 {
47            let last = parts.remove(parts.len() - 1);
48            let parts = parts.iter().map(Clone::clone).collect::<Vec<String>>();
49            (parts, last)
50        } else {
51            (Vec::<String>::new(), parts[0].to_string())
52        };
53        if folder.len() > 1 {
54            format!(
55                "{}{}{}",
56                filename,
57                $crate::color::fore(" in ", 7),
58                folder.join(std::path::MAIN_SEPARATOR_STR)
59            )
60        } else {
61            filename
62        }
63    }};
64}
65/// colorfully wraps the given text in "<", ">": "<{text}>"
66#[macro_export]
67macro_rules! tag {
68    ($arg:expr) => {{
69        $crate::tag!($arg, 7)
70    }};
71    (close, $arg:expr) => {{
72        $crate::tag!(close, $arg, 7)
73    }};
74    ($arg:expr, $color:literal) => {{
75        format!(
76            "{}{}{}",
77            $crate::color::fore("<", $color),
78            $crate::color::auto($arg),
79            $crate::color::fore(">", $color),
80        )
81    }};
82    (close, $arg:expr, $color:literal) => {{
83        format!(
84            "{}{}{}",
85            $crate::color::fore("</", $color),
86            $arg,
87            $crate::color::fore(">", $color),
88        )
89    }};
90}
91
92/// colorful alternative to [std::dbg]
93#[macro_export]
94macro_rules! dbg {
95    ($arg:expr $(,)? ) => {{
96        eprintln!("{}", $crate::format_dbg_location!($arg));
97        $arg
98    }};
99    ($( $arg:expr ),* $(,)? ) => {{
100        eprintln!("{}", $crate::format_dbg_location!($($arg),*));
101    }};
102}
103
104#[macro_export]
105macro_rules! format_dbg {
106    ($arg:expr $(,)? ) => {{
107        $crate::indent!(
108                format!(
109                    "{} = {}\n",
110                    $crate::color::auto(stringify!(&$arg)),
111                    $crate::color::auto(format!("{:#?}", &$arg))))
112
113    }};
114    ($( $arg:expr ),* $(,)? ) => {{
115        [$($crate::format_dbg!($arg)),*].join("\n")
116    }};
117}
118#[macro_export]
119macro_rules! format_dbg_location {
120    ($arg:expr $(,)? ) => {{
121        format!("{}", $crate::color::reset([$crate::location!(begin), $crate::format_dbg!($arg), $crate::location!(end)].join("\n")))
122    }};
123    ($( $arg:expr ),* $(,)? ) => {{
124        [$crate::location!(begin), $($crate::format_dbg!($arg)),*, $crate::location!(end)].join("\n")
125    }};
126}
127
128/// indents an implementor of [std::fmt::Display]
129#[macro_export]
130macro_rules! indent {
131    ($indentation:literal, $obj:expr) => {{
132        format!("{}", $obj)
133            .lines()
134            .map(|line| format!("{}{}", " ".repeat($indentation), line))
135            .collect::<Vec<String>>()
136            .join("\n")
137    }};
138    ($obj:expr) => {{
139        $crate::indent!(4, $obj)
140    }};
141}
142/// indents an implementor of [std::fmt::Debug]
143#[macro_export]
144macro_rules! indent_objdump {
145    ($indentation:literal, $obj:expr) => {{
146        format!("{:#?}", $obj)
147            .lines()
148            .map(|line| format!("{}{}", " ".repeat($indentation), line))
149            .collect::<Vec<String>>()
150            .join("\n")
151    }};
152    ($obj:expr) => {{
153        $crate::indent_objdump!(4, $obj)
154    }};
155}
156
157/// returns a [String] with the name of the function calling the macro
158#[macro_export]
159macro_rules! function_name {
160    () => {{
161        fn f() {}
162        fn type_name_of<T>(_: T) -> &'static str {
163            std::any::type_name::<T>()
164        }
165        let name = type_name_of(f);
166        let name = name
167            .strip_suffix("::f")
168            .unwrap()
169            .replace(format!("{}::", module_path!()).as_str(), "");
170        name
171    }};
172}
173
174/// colorfully steps through code
175#[macro_export]
176macro_rules! step {
177    ($text:expr $(,)?) => {{
178        $crate::step!(length=$crate::color::term_cols(), $text)
179    }};
180    (fg=$fg:expr, $text:expr $(,)?) => {{
181        $crate::step!(bg=$fg, fg=$crate::color::invert_bw($fg), length=$crate::color::term_cols(), $text)
182    }};
183    (bg=$bg:expr, fg=$fg:expr, $text:expr $(,)?) => {{
184        $crate::step!(bg=$bg, fg=$fg, length=$crate::color::term_cols(), $text)
185    }};
186    (length=$length:expr, $text:expr $(,)?) => {{
187        let (bg, fg) = $crate::color::couple(line!() as usize);
188        $crate::step!(bg=bg, fg=fg, length=$length, $text)
189    }};
190    (bg=$bg:expr, fg=$fg:expr, length=$length:expr, $text:expr $(,)?) => {{
191        let text = $text.to_string();
192        let bar = $crate::color::ansi(
193            " ".repeat($length),
194            $fg as usize,
195            $bg as usize,
196        );
197        eprintln!(
198            "\n{}",
199            [
200                bar.clone(),
201                $crate::color::ansi(
202                    $crate::color::pad_columns(
203                        [
204                            $crate::function_name!(),
205                            [
206                                file!().to_string(),
207                                line!().to_string(),
208                            ].join(":")
209                        ].join(" ").to_string()
210                    ),
211                    $fg as usize,
212                    $bg as usize,
213                ),
214                $crate::color::ansi(
215                    $crate::color::pad_columns(
216                        if text.is_empty() { String::new() } else { format!("{}", text) }
217                    ),
218                    $bg as usize,
219                    $fg as usize,
220                ),
221                bar.clone(),
222            ].join("\n")
223        );
224    }};
225    (length=$length:expr, $text:expr, $( $arg:expr ),* $(,)? ) => {{
226        $crate::step!(length=$length, format_args!($text, $($arg,)*))
227    }};
228    () => {{
229        $crate::step!("")
230    }};
231}
232/// colorfully steps through code debugging given expressions
233#[macro_export]
234macro_rules! step_dbg {
235    (bg=$bg:expr, fg=$fg:expr, length=$length:expr, $($arg:expr),* $(,)?) => {{
236        let text = format!("{}", [
237            $($crate::indent!(format!("{} = {}", $crate::color::auto(stringify!($arg)), $crate::color::auto(format!("{:#?}", $arg))))),*
238        ].join("\n"));
239        $crate::step!(bg=$bg, fg=$fg, length=$length, text);
240    }};
241    (bg=$bg:expr, fg=$fg:expr, $($arg:expr),* $(,)?) => {{
242        $crate::step_dbg!(bg=$bg, fg=$fg, length=$crate::color::term_cols(), $($arg),*)
243    }};
244    (fg=$fg:expr, $($arg:expr),* $(,)?) => {{
245        $crate::step_dbg!(bg=$fg, fg=$crate::color::invert_bw($fg), length=$crate::color::term_cols(), $($arg),*)
246    }};
247    ($($arg:expr),* $(,)?) => {{
248        let fg=$crate::color::wrap(line!() as usize);
249        $crate::step_dbg!(bg=fg, fg=$crate::color::invert_bw(fg), length=$crate::color::term_cols(), $($arg),*)
250    }};
251    () => {{
252        $crate::step!("")
253    }};
254}
255
256/// colorfully prints an admonition
257#[macro_export]
258macro_rules! admonition {
259    ($color:literal, $message:expr) => {
260        $crate::admonition!($color, "{}", $message);
261    };
262    ($color:literal, $title:literal, $message:expr) => {
263        $crate::admonition!($color, title=$title, $message);
264    };
265
266    ($color:literal, title=$title:literal, $message:expr) => {
267        $crate::admonition!($color, title=$title, "{}", $message);
268    };
269    ($color:literal, title=$title:literal, $format:literal, $($arg:expr),* $(,)?) => {{
270        use crate::color;
271        eprintln!(
272            "\n{}",
273            [
274                color::ansi(
275                    format!("{}:{} {}", crate::function_name!(), line!(), $title),
276                    color::invert_bw($color).into(),
277                    $color,
278                ),
279                color::ansi(
280                    format!($format, $($arg),*),
281                    $color,
282                    color::invert_bw($color).into(),
283                )
284            ]
285            .join(" ")
286        );
287    }};
288    ($color:literal, $format:literal, $($arg:expr),* $(,)?) => {{
289        use crate::color;
290        eprintln!(
291            "\n{}",
292            [
293                color::ansi(
294                    format!("{}:{}", crate::function_name!(), line!()),
295                    color::invert_bw($color).into(),
296                    $color,
297                ),
298                color::ansi(
299                    format!($format, $($arg),*),
300                    $color,
301                    color::invert_bw($color).into(),
302                )
303            ]
304            .join(" ")
305        );
306    }};
307}
308
309/// colorfully prints a "WARN" admonition
310#[macro_export]
311macro_rules! warn {
312    ($color:literal, $format:literal, $($arg:expr),* $(,)?) => {
313        $crate::admonition!($color, title="WARNING", $format, $($arg),*);
314    };
315    ($color:literal, $message:expr) => {
316        $crate::admonition!($color, title="WARNING", $message);
317    };
318    ($message:expr) => {
319        $crate::warn!(220, $message);
320    };
321}
322
323/// colorfully prints an "INFO" admonition
324#[macro_export]
325macro_rules! info {
326    ($color:literal, $format:literal, $($arg:expr),* $(,)?) => {
327        $crate::admonition!($color, title="INFO", $format, $($arg),*);
328    };
329    ($color:literal, $message:expr) => {
330        $crate::admonition!($color, title="INFO", $message);
331    };
332    ($message:expr) => {
333        $crate::info!(74, $message);
334    };
335}
336
337/// colorfully formats a [u8] as hex => binary => decimal (=> char (if ascii))
338#[macro_export]
339macro_rules! format_byte {
340    (hex_only, $byte:expr $(,)? ) => {{
341        use $crate::color::{auto, fore, from_byte, pad};
342        let color = $crate::color::from_byte($byte);
343        $crate::color::fore(format!("0x{:02x}", $byte), color.into())
344    }};
345    (hex, $byte:expr $(,)? ) => {{
346        use $crate::color::{auto, fore, from_bytes, pad};
347        let color = $crate::color::from_bytes(&[$byte]);
348        [
349            $crate::color::fore(format!("0x{:02x}", $byte), color.into()),
350            if $byte < 127 {
351                $crate::color::fore(
352                    format!("{:#?}", char::from($byte).to_string()),
353                    color.into(),
354                )
355            } else {
356                String::new()
357            },
358        ]
359        .iter()
360        .filter(|c| !c.is_empty())
361        .map(String::from)
362        .collect::<Vec<String>>()
363        .join(" => ")
364    }};
365    (bin, $byte:expr $(,)? ) => {{
366        use $crate::color::{auto, fore, from_bytes, pad};
367        let color = $crate::color::from_bytes(&[$byte]);
368        [
369            $crate::color::fore(format!("0b{:08b}", $byte), color.into()),
370            if $byte < 127 {
371                $crate::color::fore(
372                    format!("{:#?}", char::from($byte).to_string()),
373                    color.into(),
374                )
375            } else {
376                String::new()
377            },
378        ]
379        .iter()
380        .filter(|c| !c.is_empty())
381        .map(String::from)
382        .collect::<Vec<String>>()
383        .join(" => ")
384    }};
385    ($byte:expr $(,)? ) => {{
386        use $crate::color::{auto, fore, from_bytes, pad};
387        let color = $crate::color::from_bytes(&[$byte]);
388        [
389            $crate::color::fore(format!("0x{:02x}", $byte), color.into()),
390            $crate::color::fore(format!("0b{:08b}", $byte), color.into()),
391            $crate::color::fore(format!("{:#?}", $byte), color.into()),
392            if $byte < 127 {
393                $crate::color::fore(
394                    format!("{:#?}", char::from($byte).to_string()),
395                    color.into(),
396                )
397            } else {
398                String::new()
399            },
400        ]
401        .iter()
402        .filter(|c| !c.is_empty())
403        .map(String::from)
404        .collect::<Vec<String>>()
405        .join(" => ")
406    }};
407}
408/// [std::dbg] equivalent for u8 which uses [format_byte] to display the byte
409#[macro_export]
410macro_rules! dbg_byte {
411    ($byte:expr $(,)? ) => {{
412        use $crate::color::{auto, fore, from_display};
413        let color = $crate::color::from_display($byte);
414        $crate::step!(format!(
415            "{} = {}",
416            $crate::color::auto(stringify!($byte)),
417            $crate::format_byte!($byte)
418        ));
419        $byte
420    }};
421}
422
423/// [std::dbg] equivalent for `&[u8]` which uses [format_bytes] to display the byte slice
424#[macro_export]
425macro_rules! dbg_bytes {
426    ($slice:expr $(,)? ) => {{
427        use $crate::color::{auto, back, fore, from_display, pad};
428        $crate::step!($crate::indent!(format!(
429            "{} = {}",
430            $crate::color::auto(stringify!($slice)),
431            $crate::format_bytes!($slice)
432        )));
433        $slice
434    }};
435}
436/// [std::dbg] equivalent for `&[u8]` which uses [format_bytes] to display the byte slice in base 16 and string
437#[macro_export]
438macro_rules! dbg_bytes_str {
439    ($slice:expr $(,)? ) => {{
440        use $crate::color::{auto, back, fore, from_display, pad};
441        use $crate::indent;
442        eprintln!(
443            "\n{}",
444            [
445                $crate::location!(begin),
446                String::new(),
447                $crate::color::auto(stringify!($slice)),
448                $crate::format_bytes_str!($slice),
449                String::new(),
450                $crate::location!(end),
451            ]
452            .join("\n")
453        );
454        $slice
455    }};
456}
457/// [std::dbg_bytes_str] equivalent which only displays debug message if the given bytes are valid UTF-8
458#[macro_export]
459macro_rules! dbg_bytes_if_str {
460    ($slice:expr $(,)? ) => {
461        if let Ok(c) = std::str::from_utf8($slice) {
462            $crate::dbg_bytes!($slice)
463        } else {
464            $slice
465        }
466    };
467}
468/// colorfully formats a slice or vector of [u8] as hex => binary => decimal (=> char (if ascii))
469#[macro_export]
470macro_rules! format_bytes {
471    ($slice:expr $(,)? ) => {
472        $crate::format_bytes!($slice, " => ");
473    };
474    (hex, $slice:expr $(,)? ) => {
475        $crate::format_bytes!(hex, $slice, " => ");
476    };
477    (bin, $slice:expr $(,)? ) => {
478        $crate::format_bytes!(bin, $slice, " => ");
479    };
480    ($slice:expr, $sep:literal $(,)? ) => {{
481        [
482            format!(
483                "[\n{}]",
484                $slice
485                    .iter()
486                    .map(Clone::clone)
487                    .map(|byte| format!(
488                        "{}, // {}\n",
489                        $crate::indent!($crate::format_byte!(byte)),
490                        $crate::color::fore(format!("{:#?}", char::from(byte).to_string()), 237),
491                    ))
492                    .collect::<Vec<String>>()
493                    .join("")
494            ),
495            format!("{} bytes", $slice.len()),
496            std::str::from_utf8($slice)
497                .map(|s| {
498                    let chars = s.chars().collect::<Vec<char>>();
499                    format!(
500                        "\"{s}\" => {} chars => [{}]",
501                        chars.len(),
502                        chars
503                            .iter()
504                            .map(|c| format!("{c:?}"))
505                            .collect::<Vec<String>>()
506                            .join(", ")
507                    )
508                })
509                .unwrap_or_default(),
510        ]
511        .iter()
512        .filter(|c| !c.is_empty())
513        .map(String::from)
514        .collect::<Vec<String>>()
515        .join($sep.to_string().as_str())
516    }};
517    (hex, $slice:expr, $sep:literal $(,)? ) => {{
518        [
519            format!(
520                "[\n{}]",
521                $slice
522                    .iter()
523                    .map(Clone::clone)
524                    .map(|byte| format!(
525                        "{}, // {}\n",
526                        $crate::indent!($crate::format_byte!(hex, byte)),
527                        $crate::color::fore(format!("{:#?}", char::from(byte).to_string()), 237),
528                    ))
529                    .collect::<Vec<String>>()
530                    .join("")
531            ),
532            std::str::from_utf8($slice)
533                .map(|s| format!("{s:#?}"))
534                .unwrap_or_default(),
535        ]
536        .iter()
537        .filter(|c| !c.is_empty())
538        .map(String::from)
539        .collect::<Vec<String>>()
540        .join($sep.to_string().as_str())
541    }};
542    (bin, $slice:expr, $sep:literal $(,)? ) => {{
543        [
544            format!(
545                "[\n{}]",
546                $slice
547                    .iter()
548                    .map(Clone::clone)
549                    .map(|byte| format!(
550                        "{}, // {}\n",
551                        $crate::indent!($crate::format_byte!(bin, byte)),
552                        $crate::color::fore(format!("{:#?}", char::from(byte).to_string()), 237),
553                    ))
554                    .collect::<Vec<String>>()
555                    .join("")
556            ),
557            std::str::from_utf8($slice)
558                .map(|s| format!("{s:#?}"))
559                .unwrap_or_default(),
560        ]
561        .iter()
562        .filter(|c| !c.is_empty())
563        .map(String::from)
564        .collect::<Vec<String>>()
565        .join($sep.to_string().as_str())
566    }};
567}
568
569/// colorfully formats a slice or vector of [u8] as hex
570#[macro_export]
571macro_rules! format_bytes_str {
572    ($slice:expr $(,)? ) => {
573        $crate::format_bytes_str!($slice, " => ");
574    };
575    ($slice:expr, $sep:literal $(,)? ) => {{
576        [
577            format!(
578                "[{}]",
579                $slice
580                    .iter()
581                    .map(Clone::clone)
582                    .map(|byte| $crate::format_byte!(hex_only, byte))
583                    .collect::<Vec<String>>()
584                    .join(", ")
585            ),
586            std::str::from_utf8($slice)
587                .map(|s| format!("{s:#?}"))
588                .unwrap_or_default(),
589        ]
590        .iter()
591        .filter(|c| !c.is_empty())
592        .map(String::from)
593        .collect::<Vec<String>>()
594        .join($sep.to_string().as_str())
595    }};
596}