1use std::{collections::HashSet, fmt::Display};
4
5use brush_core::Error;
6use tracing_subscriber::{
7 Layer, Registry, filter::Targets, layer::SubscriberExt, reload::Handle, util::SubscriberInitExt,
8};
9
10#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, clap::ValueEnum)]
12pub enum TraceEvent {
13 #[clap(name = "arithmetic")]
15 Arithmetic,
16 #[clap(name = "commands")]
18 Commands,
19 #[clap(name = "complete")]
21 Complete,
22 #[clap(name = "expand")]
24 Expand,
25 #[clap(name = "functions")]
27 Functions,
28 #[clap(name = "input")]
30 Input,
31 #[clap(name = "jobs")]
33 Jobs,
34 #[clap(name = "parse")]
36 Parse,
37 #[clap(name = "pattern")]
39 Pattern,
40 #[clap(name = "tokenize")]
42 Tokenize,
43 #[clap(name = "unimplemented", alias = "unimp")]
45 Unimplemented,
46}
47
48impl Display for TraceEvent {
49 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
50 match self {
51 Self::Arithmetic => write!(f, "arithmetic"),
52 Self::Commands => write!(f, "commands"),
53 Self::Complete => write!(f, "complete"),
54 Self::Expand => write!(f, "expand"),
55 Self::Functions => write!(f, "functions"),
56 Self::Input => write!(f, "input"),
57 Self::Jobs => write!(f, "jobs"),
58 Self::Parse => write!(f, "parse"),
59 Self::Pattern => write!(f, "pattern"),
60 Self::Tokenize => write!(f, "tokenize"),
61 Self::Unimplemented => write!(f, "unimplemented"),
62 }
63 }
64}
65
66#[derive(Default)]
67pub(crate) struct TraceEventConfig {
68 enabled_debug_events: HashSet<TraceEvent>,
69 disabled_events: HashSet<TraceEvent>,
70 handle: Option<Handle<Targets, Registry>>,
71}
72
73impl TraceEventConfig {
74 pub fn init(enabled_debug_events: &[TraceEvent], disabled_events: &[TraceEvent]) -> Self {
75 let enabled_debug_events: HashSet<TraceEvent> =
76 enabled_debug_events.iter().copied().collect();
77 let disabled_events: HashSet<TraceEvent> = disabled_events.iter().copied().collect();
78
79 let mut config = Self {
80 enabled_debug_events,
81 disabled_events,
82 ..Default::default()
83 };
84
85 let filter = config.compose_filter();
86
87 let (reload_filter, handle) = tracing_subscriber::reload::Layer::new(filter);
89
90 let layer = tracing_subscriber::fmt::layer()
91 .with_writer(std::io::stderr)
92 .without_time()
93 .with_target(false)
94 .with_filter(reload_filter);
95
96 if tracing_subscriber::registry()
97 .with(layer)
98 .try_init()
99 .is_ok()
100 {
101 config.handle = Some(handle);
102 } else {
103 eprintln!("warning: failed to initialize tracing.");
105 }
106
107 config
108 }
109
110 fn compose_filter(&self) -> tracing_subscriber::filter::Targets {
111 let mut filter = tracing_subscriber::filter::Targets::new()
112 .with_default(tracing_subscriber::filter::LevelFilter::INFO);
113
114 for event in &self.enabled_debug_events {
115 let targets = Self::event_to_tracing_targets(event);
116 filter = filter.with_targets(
117 targets
118 .into_iter()
119 .map(|target| (target, tracing::Level::DEBUG)),
120 );
121 }
122
123 for event in &self.disabled_events {
124 let targets = Self::event_to_tracing_targets(event);
125 filter = filter.with_targets(
126 targets
127 .into_iter()
128 .map(|target| (target, tracing::level_filters::LevelFilter::OFF)),
129 );
130 }
131
132 filter
133 }
134
135 fn event_to_tracing_targets(event: &TraceEvent) -> Vec<&str> {
136 match event {
137 TraceEvent::Arithmetic => vec!["arithmetic"],
138 TraceEvent::Commands => vec!["commands"],
139 TraceEvent::Complete => vec!["completion"],
140 TraceEvent::Expand => vec!["expansion"],
141 TraceEvent::Functions => vec!["functions"],
142 TraceEvent::Input => vec!["input"],
143 TraceEvent::Jobs => vec!["jobs"],
144 TraceEvent::Parse => vec!["parse"],
145 TraceEvent::Pattern => vec!["pattern"],
146 TraceEvent::Tokenize => vec!["tokenize"],
147 TraceEvent::Unimplemented => vec!["unimplemented"],
148 }
149 }
150
151 pub const fn get_enabled_events(&self) -> &HashSet<TraceEvent> {
152 &self.enabled_debug_events
153 }
154
155 pub fn enable(&mut self, event: TraceEvent) -> Result<(), Error> {
156 if !self.enabled_debug_events.insert(event) {
158 return Ok(());
159 }
160
161 self.reload_filter()
162 }
163
164 pub fn disable(&mut self, event: TraceEvent) -> Result<(), Error> {
165 if !self.enabled_debug_events.remove(&event) {
167 return Ok(());
168 }
169
170 self.reload_filter()
171 }
172
173 fn reload_filter(&self) -> Result<(), Error> {
174 if let Some(handle) = &self.handle {
175 if handle.reload(self.compose_filter()).is_ok() {
176 Ok(())
177 } else {
178 Err(brush_core::ErrorKind::Unimplemented("failed to enable tracing events").into())
179 }
180 } else {
181 Err(brush_core::ErrorKind::Unimplemented("tracing not initialized").into())
182 }
183 }
184}