cargo_e/
e_eventdispatcher.rs1use regex::Regex;
2use std::cell::RefCell;
3use std::fmt;
4use std::sync::atomic::AtomicBool;
5use std::sync::atomic::Ordering;
6use std::sync::Arc;
7use std::sync::Mutex;
8
9use crate::e_cargocommand_ext::CargoStats;
10use crate::e_command_builder::TerminalError;
11
12#[derive(Debug, Clone, PartialEq, Eq, Hash, Copy)]
14pub enum CargoDiagnosticLevel {
15 Error,
16 Warning,
17 Help,
18 Note,
19}
20
21#[derive(Debug, Clone)]
55pub enum CallbackType {
56 LevelMessage,
57 Warning,
58 Error,
59 Help,
60 Note,
61 Location,
62 OpenedUrl,
63 Unspecified,
64 Suggestion,
65}
66
67#[derive(Debug, Clone)]
69pub struct CallbackResponse {
70 pub callback_type: CallbackType,
71 pub message: Option<String>,
72 pub file: Option<String>,
73 pub line: Option<usize>,
74 pub column: Option<usize>,
75 pub suggestion: Option<String>,
76 pub terminal_status: Option<TerminalError>,
77}
78
79#[derive(Clone)]
80pub struct PatternCallback {
81 pub pattern: Regex,
82 pub callback: Arc<
84 dyn Fn(
85 &str,
86 Option<regex::Captures>,
87 Arc<AtomicBool>,
88 Arc<Mutex<CargoStats>>,
89 Option<CallbackResponse>,
90 ) -> Option<CallbackResponse>
91 + Send
92 + Sync,
93 >,
94 pub is_reading_multiline: Arc<AtomicBool>,
95}
96
97impl fmt::Debug for PatternCallback {
98 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
99 f.debug_struct("PatternCallback")
100 .field("pattern", &self.pattern.as_str())
101 .field("callback", &"Closure")
102 .finish()
103 }
104}
105
106impl PatternCallback {
107 pub fn new(
108 pattern: &str,
109 callback: Box<
110 dyn Fn(
111 &str,
112 Option<regex::Captures>,
113 Arc<AtomicBool>,
114 Arc<Mutex<CargoStats>>,
115 Option<CallbackResponse>,
116 ) -> Option<CallbackResponse>
117 + Send
118 + Sync,
119 >,
120 ) -> Self {
121 PatternCallback {
122 pattern: Regex::new(pattern).expect("Invalid regex"),
123 callback: Arc::new(callback),
124 is_reading_multiline: Arc::new(AtomicBool::new(false)),
125 }
126 }
127}
128
129#[derive(Clone, Debug)]
131pub struct EventDispatcher {
132 pub callbacks: Arc<Mutex<Vec<PatternCallback>>>,
133}
134
135impl EventDispatcher {
136 pub fn new() -> Self {
137 EventDispatcher {
138 callbacks: Arc::new(Mutex::new(Vec::new())),
139 }
140 }
141
142 pub fn add_callback(
144 &mut self,
145 pattern: &str,
146 callback: Box<
147 dyn Fn(
148 &str,
149 Option<regex::Captures>,
150 Arc<AtomicBool>,
151 Arc<Mutex<CargoStats>>,
152 Option<CallbackResponse>,
153 ) -> Option<CallbackResponse>
154 + Send
155 + Sync,
156 >,
157 ) {
158 if let Ok(mut callbacks) = self.callbacks.lock() {
159 callbacks.push(PatternCallback::new(pattern, callback));
160 } else {
161 eprintln!("Failed to acquire lock on callbacks in add_callback");
162 }
163 }
164
165 pub fn dispatch(
167 &self,
168 line: &str,
169 stats: Arc<Mutex<CargoStats>>,
170 ) -> Vec<Option<CallbackResponse>> {
171 let mut responses = Vec::new();
172 thread_local! {
174 static PRIOR_RESPONSE: RefCell<Option<CallbackResponse>> = RefCell::new(None);
175 }
176 if let Ok(callbacks) = self.callbacks.lock() {
177 for cb in callbacks.iter() {
178 let is_reading_multiline = Arc::clone(&cb.is_reading_multiline);
179 let prior = PRIOR_RESPONSE.with(|p| p.borrow().clone());
180 let response = if is_reading_multiline.load(Ordering::Relaxed) {
181 (cb.callback)(
183 line,
184 None,
185 Arc::clone(&is_reading_multiline),
186 stats.clone(),
187 prior,
188 )
189 } else if let Some(captures) = cb.pattern.captures(line) {
190 (cb.callback)(
191 line,
192 Some(captures),
193 Arc::clone(&is_reading_multiline),
194 stats.clone(),
195 None,
196 )
197 } else if cb.pattern.is_match(line) {
198 (cb.callback)(
199 line,
200 None,
201 Arc::clone(&is_reading_multiline),
202 stats.clone(),
203 None,
204 )
205 } else {
206 None
207 };
208 if is_reading_multiline.load(Ordering::Relaxed) {
209 PRIOR_RESPONSE.with(|p| *p.borrow_mut() = response.clone());
210 }
211 responses.push(response);
212 }
213 } else {
214 eprintln!("Failed to acquire lock on callbacks in dispatch");
215 }
216 responses
217 }
218
219 pub fn process_stream<R: std::io::BufRead>(
221 &self,
222 reader: R,
223 stats: Arc<Mutex<CargoStats>>,
224 ) -> Vec<CallbackResponse> {
225 let mut responses = Vec::new();
226 for line in reader.lines() {
227 if let Ok(line) = line {
228 let res = self.dispatch(&line, Arc::clone(&stats));
229 for r in res {
230 if let Some(cb) = r {
231 responses.push(cb);
232 }
233 }
234 }
235 }
236 responses
237 }
238}