1#![allow(clippy::print_stdout)]
2use std::cell::RefCell;
3use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
4use std::sync::LazyLock;
5use std::time::Instant;
6
7pub static ENABLE_TRACE: LazyLock<AtomicBool> = LazyLock::new(|| AtomicBool::new(false));
9
10thread_local! {
12 static INDENT: RefCell<usize> = const { RefCell::new(0) };
13}
14
15static CALL_ID_COUNTER: AtomicUsize = AtomicUsize::new(1);
16
17pub fn with_indent<F: FnOnce(usize) -> String>(f: F) {
19 INDENT.with(|level| {
20 let indent = *level.borrow();
21 println!("{}", f(indent));
22 });
23}
24
25fn indent_inc() {
27 INDENT.with(|i| *i.borrow_mut() += 1);
28}
29
30fn indent_dec() {
32 INDENT.with(|i| *i.borrow_mut() -= 1);
33}
34
35pub struct TraceFnGuard {
37 name: &'static str,
38 enabled: bool,
39 start_time: Option<Instant>,
40 id: usize,
41}
42
43impl TraceFnGuard {
44 pub fn new(name: &'static str, message: Option<&str>) -> Self {
45 let enabled = ENABLE_TRACE.load(Ordering::Relaxed);
46 let start_time = if enabled { Some(Instant::now()) } else { None };
47 let id = CALL_ID_COUNTER.fetch_add(1, Ordering::Relaxed);
48
49 if enabled {
50 with_indent(|i| format!(
51 "{}[#{}]↳ Enter: {} {}",
52 " ".repeat(i),
53 id,
54 name,
55 message.unwrap_or("")
56 ));
57 indent_inc();
58 }
59
60 Self {
61 name,
62 enabled,
63 start_time,
64 id,
65 }
66 }
67}
68
69impl Drop for TraceFnGuard {
70 fn drop(&mut self) {
71 if self.enabled {
72 indent_dec();
73 if let Some(start) = self.start_time {
74 let duration = start.elapsed();
75 with_indent(|i| format!(
76 "{}[#{}]↳ Exit: {} (took {:.2?})",
77 " ".repeat(i),
78 self.id,
79 self.name,
80 duration
81 ));
82 } else {
83 with_indent(|i| format!(
84 "{}↳ Exit: {}",
85 " ".repeat(i),
86 self.name
87 ));
88 }
89 }
90 }
91}
92
93#[macro_export]
95macro_rules! trace_fn {
96 ($name:expr) => {
97 let _trace_guard = $crate::TraceFnGuard::new($name, None);
98 };
99 ($name:expr, $($tt:tt)*) => {
100 let _trace_guard = $crate::TraceFnGuard::new($name, Some(&format!($($tt)*)));
101 };
102}
103
104#[macro_export]
105macro_rules! trace_log {
106 ($($tt:tt)*) => {
107 if $crate::ENABLE_TRACE.load(std::sync::atomic::Ordering::Relaxed) {
108 $crate::with_indent(|i| format!(
109 "{} {}",
110 " ".repeat(i),
111 format!($($tt)*),
112 ));
113 }
114 };
115}