cargo_e/
e_eventdispatcher.rs

1use regex::Regex;
2use std::fmt;
3use std::sync::atomic::AtomicBool;
4use std::sync::atomic::Ordering;
5use std::sync::Arc;
6use std::sync::Mutex;
7
8use crate::e_cargocommand_ext::CargoStats;
9use crate::e_command_builder::TerminalError;
10
11/// Our internal diagnostic level for cargo.
12#[derive(Debug, Clone, PartialEq, Eq, Hash, Copy)]
13pub enum CargoDiagnosticLevel {
14    Error,
15    Warning,
16    Help,
17    Note,
18}
19
20// /// A line of source code associated with a diagnostic.
21// #[derive(Debug, Clone)]
22// pub struct CargoDiagnosticSpanLine {
23//     pub text: String,
24//     pub highlight_start: usize,
25//     pub highlight_end: usize,
26// }
27
28// A span (i.e. file location) associated with a diagnostic.
29// #[derive(Debug, Clone)]
30// pub struct CargoDiagnosticSpan {
31//     pub file_name: String,
32//     pub line_start: usize,
33//     pub line_end: usize,
34//     pub column_start: usize,
35//     pub column_end: usize,
36//     pub is_primary: bool,
37//     pub text: Vec<CargoDiagnosticSpanLine>,
38//     pub label: Option<String>,
39//     pub suggested_replacement: Option<String>,
40// }
41
42// /// Our internal diagnostic message.
43// #[derive(Debug, Clone)]
44// pub struct CargoDiagnostic {
45//     pub message: String,
46//     pub code: Option<String>,
47//     pub level: CargoDiagnosticLevel,
48//     pub spans: Vec<CargoDiagnosticSpan>,
49//     pub children: Vec<CargoDiagnostic>,
50// }
51
52/// Our callback type enum.
53#[derive(Debug, Clone)]
54pub enum CallbackType {
55    LevelMessage,
56    Warning,
57    Error,
58    Help,
59    Note,
60    Location,
61    OpenedUrl,
62    Unspecified,
63    Suggestion,
64}
65
66/// The callback response produced by our event dispatcher.
67#[derive(Debug, Clone)]
68pub struct CallbackResponse {
69    pub callback_type: CallbackType,
70    pub message: Option<String>,
71    pub file: Option<String>,
72    pub line: Option<usize>,
73    pub column: Option<usize>,
74    pub suggestion: Option<String>,
75    pub terminal_status: Option<TerminalError>,
76}
77
78#[derive(Clone)]
79pub struct PatternCallback {
80    pub pattern: Regex,
81    // pub callback: Arc<dyn Fn(&str) -> Option<CallbackResponse> + Send + Sync>,
82    pub callback: Arc<
83        dyn Fn(
84                &str,
85                Option<regex::Captures>,
86                Arc<AtomicBool>,
87                Arc<Mutex<CargoStats>>,
88            ) -> Option<CallbackResponse>
89            + Send
90            + Sync,
91    >,
92    pub is_reading_multiline: Arc<AtomicBool>,
93}
94
95impl fmt::Debug for PatternCallback {
96    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
97        f.debug_struct("PatternCallback")
98            .field("pattern", &self.pattern.as_str())
99            .field("callback", &"Closure")
100            .finish()
101    }
102}
103
104impl PatternCallback {
105    pub fn new(
106        pattern: &str,
107        callback: Box<
108            dyn Fn(
109                    &str,
110                    Option<regex::Captures>,
111                    Arc<AtomicBool>,
112                    Arc<Mutex<CargoStats>>,
113                ) -> Option<CallbackResponse>
114                + Send
115                + Sync,
116        >,
117    ) -> Self {
118        PatternCallback {
119            pattern: Regex::new(pattern).expect("Invalid regex"),
120            callback: Arc::new(callback),
121            is_reading_multiline: Arc::new(AtomicBool::new(false)),
122        }
123    }
124}
125
126/// A simple event dispatcher for output lines.
127#[derive(Clone, Debug)]
128pub struct EventDispatcher {
129    pub callbacks: Arc<Mutex<Vec<PatternCallback>>>,
130}
131
132impl EventDispatcher {
133    pub fn new() -> Self {
134        EventDispatcher {
135            callbacks: Arc::new(Mutex::new(Vec::new())),
136        }
137    }
138
139    /// Add a new callback with a regex pattern.
140    pub fn add_callback(
141        &mut self,
142        pattern: &str,
143        callback: Box<
144            dyn Fn(
145                    &str,
146                    Option<regex::Captures>,
147                    Arc<AtomicBool>,
148                    Arc<Mutex<CargoStats>>,
149                ) -> Option<CallbackResponse>
150                + Send
151                + Sync,
152        >,
153    ) {
154        if let Ok(mut callbacks) = self.callbacks.lock() {
155            callbacks.push(PatternCallback::new(pattern, callback));
156        } else {
157            eprintln!("Failed to acquire lock on callbacks in add_callback");
158        }
159    }
160
161    /// Dispatch a line to all callbacks that match, and collect their responses.
162    pub fn dispatch(
163        &self,
164        line: &str,
165        stats: Arc<Mutex<CargoStats>>,
166    ) -> Vec<Option<CallbackResponse>> {
167        let mut responses = Vec::new();
168        if let Ok(callbacks) = self.callbacks.lock() {
169            for cb in callbacks.iter() {
170                let is_reading_multiline = Arc::clone(&cb.is_reading_multiline);
171                if is_reading_multiline.load(Ordering::Relaxed) {
172                    // The callback is in multiline mode. Call it with no captures.
173                    let response = (cb.callback)(line, None, is_reading_multiline, stats.clone());
174                    responses.push(response);
175                } else if let Some(captures) = cb.pattern.captures(line) {
176                    // The line matches the callback's pattern.
177                    let response =
178                        (cb.callback)(line, Some(captures), is_reading_multiline, stats.clone());
179                    responses.push(response);
180                } else if cb.pattern.is_match(line) {
181                    // If there are no captures but there's a match, pass None to the callback
182                    let response = (cb.callback)(line, None, is_reading_multiline, stats.clone());
183                    responses.push(response);
184                }
185            }
186        } else {
187            eprintln!("Failed to acquire lock on callbacks in dispatch");
188        }
189        responses
190    }
191}