1use std::cell::Cell;
9use std::fmt;
10use std::time::{Duration, Instant};
11
12thread_local! {
14 static INDENT_LEVEL: Cell<usize> = const { Cell::new(0) };
15}
16
17#[derive(Debug, Clone, PartialEq)]
19pub struct ScopeError(pub &'static str);
20
21pub struct ScopeGuard {
23 name: &'static str,
24 start: Instant,
25 indent: usize,
26 exited: bool,
27}
28
29impl ScopeGuard {
30 pub fn enter(name: &'static str) -> Self {
32 let indent = INDENT_LEVEL.with(|lvl| {
33 let current = lvl.get();
34 lvl.set(current + 1);
35 current + 1
36 });
37 let start = Instant::now();
38 ScopeGuard {
40 name,
41 start,
42 indent,
43 exited: false,
44 }
45 }
46
47 pub fn elapsed(&self) -> Duration {
49 self.start.elapsed()
50 }
51
52 pub fn indent_level() -> usize {
54 INDENT_LEVEL.with(|lvl| lvl.get())
55 }
56
57 pub fn exit(mut self) {
59 self.exited = true;
60 }
63}
64
65impl Drop for ScopeGuard {
66 fn drop(&mut self) {
67 INDENT_LEVEL.with(|lvl| {
69 let current = lvl.get();
70 if current > 0 {
71 lvl.set(current - 1);
72 }
73 });
74 if !self.exited {
77 self.exited = true;
78 }
79 }
80}
81
82impl fmt::Debug for ScopeGuard {
83 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
84 f.debug_struct("ScopeGuard")
85 .field("name", &self.name)
86 .field("indent", &self.indent)
87 .field("elapsed", &self.elapsed())
88 .finish()
89 }
90}
91
92#[cfg(test)]
93mod tests {
94 use super::*;
95 use std::panic;
96 use std::thread;
97 use std::time::Duration;
98
99 #[test]
100 fn test_scope_basic_timing() {
101 let start = Instant::now();
102 let guard = ScopeGuard::enter("test_scope");
103 thread::sleep(Duration::from_millis(10));
104 let elapsed = guard.elapsed();
105 assert!(elapsed >= Duration::from_millis(10));
106 drop(guard);
107 let total = start.elapsed();
108 assert!(total >= Duration::from_millis(10));
109 }
110
111 #[test]
112 fn test_scope_indentation() {
113 assert_eq!(ScopeGuard::indent_level(), 0);
114 {
115 let _g1 = ScopeGuard::enter("outer");
116 assert_eq!(ScopeGuard::indent_level(), 1);
117 {
118 let _g2 = ScopeGuard::enter("inner");
119 assert_eq!(ScopeGuard::indent_level(), 2);
120 }
121 assert_eq!(ScopeGuard::indent_level(), 1);
122 }
123 assert_eq!(ScopeGuard::indent_level(), 0);
124 }
125
126 #[test]
127 fn test_scope_exit_explicit() {
128 let guard = ScopeGuard::enter("explicit_exit");
129 guard.exit();
130 assert_eq!(ScopeGuard::indent_level(), 0);
132 }
133
134 #[test]
135 fn test_scope_panic_handling() {
136 let result = panic::catch_unwind(|| {
137 let _guard = ScopeGuard::enter("panic_scope");
138 panic!("test panic");
139 });
140 assert!(result.is_err());
141 assert_eq!(ScopeGuard::indent_level(), 0);
143 }
144}