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