hojicha_core/debug/
inspector.rs

1//! Message and command inspection utilities
2
3use std::fmt::Debug;
4
5/// Inspector for examining values as they flow through the system
6pub struct Inspector {
7    enabled: bool,
8    prefix: String,
9}
10
11impl Inspector {
12    /// Create a new inspector
13    pub fn new() -> Self {
14        Self {
15            enabled: true,
16            prefix: String::from("[INSPECT]"),
17        }
18    }
19
20    /// Create an inspector with a custom prefix
21    pub fn with_prefix(prefix: impl Into<String>) -> Self {
22        Self {
23            enabled: true,
24            prefix: prefix.into(),
25        }
26    }
27
28    /// Enable or disable the inspector
29    pub fn set_enabled(&mut self, enabled: bool) {
30        self.enabled = enabled;
31    }
32
33    /// Inspect a value, printing it to stderr
34    pub fn inspect<T: Debug>(&self, label: &str, value: &T) {
35        if self.enabled {
36            eprintln!("{} {}: {:?}", self.prefix, label, value);
37        }
38    }
39
40    /// Inspect a value with a custom formatter
41    pub fn inspect_with<T, F>(&self, label: &str, value: &T, formatter: F)
42    where
43        F: FnOnce(&T) -> String,
44    {
45        if self.enabled {
46            eprintln!("{} {}: {}", self.prefix, label, formatter(value));
47        }
48    }
49
50    /// Create a scoped inspector for a specific operation
51    pub fn scope(&self, scope_name: &str) -> ScopedInspector {
52        ScopedInspector {
53            inspector: self,
54            scope_name: scope_name.to_string(),
55            depth: 0,
56        }
57    }
58}
59
60impl Default for Inspector {
61    fn default() -> Self {
62        Self::new()
63    }
64}
65
66/// A scoped inspector that adds context to inspection output
67pub struct ScopedInspector<'a> {
68    inspector: &'a Inspector,
69    scope_name: String,
70    depth: usize,
71}
72
73impl<'a> ScopedInspector<'a> {
74    /// Inspect a value within this scope
75    pub fn inspect<T: Debug>(&self, label: &str, value: &T) {
76        if self.inspector.enabled {
77            let indent = "  ".repeat(self.depth);
78            eprintln!(
79                "{} {}{}/{}: {:?}",
80                self.inspector.prefix, indent, self.scope_name, label, value
81            );
82        }
83    }
84
85    /// Create a nested scope
86    pub fn nested(&self, name: &str) -> ScopedInspector {
87        ScopedInspector {
88            inspector: self.inspector,
89            scope_name: format!("{}/{}", self.scope_name, name),
90            depth: self.depth + 1,
91        }
92    }
93}
94
95/// Extension trait for adding inspection to command chains
96pub trait Inspectable: Sized {
97    /// Inspect this value with a label
98    fn inspect(self, label: &str) -> Self
99    where
100        Self: Debug,
101    {
102        eprintln!("[INSPECT] {}: {:?}", label, self);
103        self
104    }
105
106    /// Conditionally inspect this value
107    fn inspect_if(self, condition: bool, label: &str) -> Self
108    where
109        Self: Debug,
110    {
111        if condition {
112            eprintln!("[INSPECT] {}: {:?}", label, self);
113        }
114        self
115    }
116
117    /// Inspect with a custom formatter
118    fn inspect_with<F>(self, label: &str, f: F) -> Self
119    where
120        F: FnOnce(&Self) -> String,
121    {
122        eprintln!("[INSPECT] {}: {}", label, f(&self));
123        self
124    }
125
126    /// Tap into the value for side effects
127    fn tap<F>(self, f: F) -> Self
128    where
129        F: FnOnce(&Self),
130    {
131        f(&self);
132        self
133    }
134
135    /// Conditionally tap into the value
136    fn tap_if<F>(self, condition: bool, f: F) -> Self
137    where
138        F: FnOnce(&Self),
139    {
140        if condition {
141            f(&self);
142        }
143        self
144    }
145}
146
147// Implement for all types
148impl<T> Inspectable for T {}