rust_loguru/
scope.rs

1//! Scope management for rust-loguru
2//!
3//! - Timed execution scopes
4//! - Indentation management
5//! - Enter/exit tracking
6//! - Performance timing
7
8use std::cell::Cell;
9use std::fmt;
10use std::time::{Duration, Instant};
11
12// Thread-local indentation level for scopes
13thread_local! {
14    static INDENT_LEVEL: Cell<usize> = const { Cell::new(0) };
15}
16
17/// Error type for scope operations
18#[derive(Debug, Clone, PartialEq)]
19pub struct ScopeError(pub &'static str);
20
21/// A guard that tracks the lifetime of a scope, measures timing, and manages indentation
22pub struct ScopeGuard {
23    name: &'static str,
24    start: Instant,
25    indent: usize,
26    exited: bool,
27}
28
29impl ScopeGuard {
30    /// Enter a new scope with the given name
31    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        // Optionally: log entering scope here
39        ScopeGuard {
40            name,
41            start,
42            indent,
43            exited: false,
44        }
45    }
46
47    /// Get the elapsed time since entering the scope
48    pub fn elapsed(&self) -> Duration {
49        self.start.elapsed()
50    }
51
52    /// Get the current indentation level
53    pub fn indent_level() -> usize {
54        INDENT_LEVEL.with(|lvl| lvl.get())
55    }
56
57    /// Explicitly exit the scope (optional, usually handled by Drop)
58    pub fn exit(mut self) {
59        self.exited = true;
60        // Optionally: log exiting scope here
61        // Indentation will be handled in Drop
62    }
63}
64
65impl Drop for ScopeGuard {
66    fn drop(&mut self) {
67        // Decrement indentation
68        INDENT_LEVEL.with(|lvl| {
69            let current = lvl.get();
70            if current > 0 {
71                lvl.set(current - 1);
72            }
73        });
74        // Optionally: log scope exit and timing
75        // If not already exited, mark as exited
76        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        // Indentation should still be correct after drop
131        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        // Indentation should be reset after panic
142        assert_eq!(ScopeGuard::indent_level(), 0);
143    }
144}