opencrates 3.0.1

Enterprise-grade AI-powered Rust development companion with comprehensive automation, monitoring, and deployment capabilities
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
//! Event handling system for the OpenCrates TUI
//!
//! This module provides comprehensive event handling for terminal user interfaces,
//! including keyboard input, mouse interactions, and periodic update events.

use anyhow::Result;
use crossterm::event::{self, Event as CrosstermEvent, KeyEvent, MouseEvent};
use std::time::Duration;
use tokio::sync::mpsc::{self, UnboundedReceiver, UnboundedSender};
use tokio::time::interval;
use tracing::{debug, error, warn};

/// Events that can be handled by the TUI application
/// Provides a unified interface for different types of user and system events
#[derive(Debug, Clone)]
pub enum Event {
    /// Keyboard input event containing key information
    Key(KeyEvent),
    /// Mouse input event for click and movement interactions
    Mouse(MouseEvent),
    /// Periodic tick event for animations and regular updates
    Tick,
    /// Terminal resize event with new dimensions
    Resize(u16, u16),
    /// Application quit request
    Quit,
    /// Custom application events
    Custom(String),
}

/// Event handler that manages all TUI events
/// Processes terminal events and generates periodic tick events for smooth updates
pub struct EventHandler {
    /// Receiver for events from the event processing task
    receiver: UnboundedReceiver<Event>,
    /// Sender for events (used internally)
    sender: UnboundedSender<Event>,
    /// Handle to the background event processing task
    _task: tokio::task::JoinHandle<()>,
}

impl EventHandler {
    /// Creates a new event handler with the specified tick rate
    ///
    /// # Arguments
    /// * `tick_rate_ms` - Milliseconds between tick events for smooth animations
    ///
    /// # Returns
    /// A new `EventHandler` instance ready to process events
    pub fn new(tick_rate_ms: u64) -> Self {
        let tick_rate = Duration::from_millis(tick_rate_ms);
        let (sender, receiver) = mpsc::unbounded_channel();

        debug!(
            "Initializing event handler with tick rate: {}ms",
            tick_rate_ms
        );

        let task_sender = sender.clone();
        let task = tokio::spawn(async move {
            Self::event_loop(task_sender, tick_rate).await;
        });

        Self {
            receiver,
            sender,
            _task: task,
        }
    }

    /// Gets the next event from the event queue
    ///
    /// # Returns
    /// The next available event
    ///
    /// # Errors
    /// Returns an error if event reception fails
    pub async fn next(&mut self) -> Result<Event> {
        if let Some(event) = self.receiver.recv().await {
            debug!("Received event: {:?}", event);
            Ok(event)
        } else {
            error!("Event channel closed unexpectedly");
            Err(anyhow::anyhow!("Event channel closed"))
        }
    }

    /// Sends a custom event to the event queue
    ///
    /// # Arguments
    /// * `event` - The event to send
    ///
    /// # Errors
    /// Returns an error if event sending fails
    pub fn send(&self, event: Event) -> Result<()> {
        match self.sender.send(event) {
            Ok(()) => Ok(()),
            Err(e) => {
                error!("Failed to send event: {}", e);
                Err(anyhow::anyhow!("Failed to send event: {}", e))
            }
        }
    }

    /// Checks if there are pending events without blocking
    ///
    /// # Returns
    /// True if events are available, false otherwise
    #[must_use]
    pub fn has_pending_events(&self) -> bool {
        !self.receiver.is_empty()
    }

    /// Gets the number of pending events in the queue
    ///
    /// # Returns
    /// Number of events waiting to be processed
    #[must_use]
    pub fn pending_event_count(&self) -> usize {
        // Note: This is an approximation as tokio doesn't provide exact count
        if self.receiver.is_empty() {
            0
        } else {
            1
        }
    }

    /// Sends a quit event to gracefully shutdown the event loop
    ///
    /// # Errors
    /// Returns an error if the quit event cannot be sent
    pub fn quit(&self) -> Result<()> {
        debug!("Sending quit event");
        self.send(Event::Quit)
    }

    /// Sends a custom application event
    ///
    /// # Arguments
    /// * `message` - Custom event message
    ///
    /// # Errors
    /// Returns an error if the custom event cannot be sent
    pub fn send_custom(&self, message: String) -> Result<()> {
        debug!("Sending custom event: {}", message);
        self.send(Event::Custom(message))
    }

    /// Main event processing loop that runs in the background
    ///
    /// # Arguments
    /// * `sender` - Channel sender for distributing events
    /// * `tick_rate` - Duration between tick events
    async fn event_loop(sender: UnboundedSender<Event>, tick_rate: Duration) {
        debug!("Starting event processing loop");

        let mut tick_interval = interval(tick_rate);
        let mut event_count = 0u64;
        let mut last_resize = None;

        loop {
            tokio::select! {
                // Handle periodic tick events
                _ = tick_interval.tick() => {
                    event_count += 1;
                    if event_count % 100 == 0 {
                        debug!("Processed {} events", event_count);
                    }

                    if let Err(e) = sender.send(Event::Tick) {
                        error!("Failed to send tick event: {}", e);
                        break;
                    }
                }

                // Handle terminal events
                event_result = Self::read_terminal_event() => {
                    match event_result {
                        Ok(Some(event)) => {
                            // Handle resize event deduplication
                            if let Event::Resize(w, h) = &event {
                                if last_resize == Some((*w, *h)) {
                                    continue; // Skip duplicate resize events
                                }
                                last_resize = Some((*w, *h));
                                debug!("Terminal resized to {}x{}", w, h);
                            }

                            if let Err(e) = sender.send(event) {
                                error!("Failed to send terminal event: {}", e);
                                break;
                            }
                        }
                        Ok(None) => {
                            // No event available, continue
                        }
                        Err(e) => {
                            error!("Error reading terminal event: {}", e);
                            // Continue processing despite errors
                        }
                    }
                }
            }
        }

        debug!("Event processing loop terminated");
    }

    /// Reads and converts terminal events
    ///
    /// # Returns
    /// Optional event if one is available, or None if no events are pending
    ///
    /// # Errors
    /// Returns an error if event reading fails
    async fn read_terminal_event() -> Result<Option<Event>> {
        // Check if an event is available without blocking
        if !event::poll(Duration::from_millis(1))? {
            return Ok(None);
        }

        match event::read()? {
            CrosstermEvent::Key(key_event) => {
                // Only process key press events to avoid duplicates
                if key_event.kind == event::KeyEventKind::Press {
                    Ok(Some(Event::Key(key_event)))
                } else {
                    Ok(None)
                }
            }
            CrosstermEvent::Mouse(mouse_event) => Ok(Some(Event::Mouse(mouse_event))),
            CrosstermEvent::Resize(width, height) => Ok(Some(Event::Resize(width, height))),
            CrosstermEvent::FocusGained => {
                debug!("Terminal focus gained");
                Ok(Some(Event::Custom("focus_gained".to_string())))
            }
            CrosstermEvent::FocusLost => {
                debug!("Terminal focus lost");
                Ok(Some(Event::Custom("focus_lost".to_string())))
            }
            CrosstermEvent::Paste(text) => {
                debug!("Text pasted: {} characters", text.len());
                Ok(Some(Event::Custom(format!("paste:{text}"))))
            }
        }
    }

    /// Creates an event handler optimized for smooth animations
    ///
    /// # Returns
    /// `EventHandler` with high refresh rate (60 FPS)
    #[must_use]
    pub fn new_smooth() -> Self {
        Self::new(16) // ~60 FPS
    }

    /// Creates an event handler optimized for low CPU usage
    ///
    /// # Returns
    /// `EventHandler` with moderate refresh rate (30 FPS)
    #[must_use]
    pub fn new_efficient() -> Self {
        Self::new(33) // ~30 FPS
    }

    /// Creates an event handler for debugging with verbose logging
    ///
    /// # Returns
    /// `EventHandler` with detailed event logging
    pub fn new_debug() -> Self {
        debug!("Creating debug event handler");
        Self::new(100) // 10 FPS for easier debugging
    }

    /// Flushes all pending events from the queue
    ///
    /// # Returns
    /// Number of events that were flushed
    pub async fn flush_events(&mut self) -> usize {
        let mut count = 0;
        while let Ok(event) =
            tokio::time::timeout(Duration::from_millis(1), self.receiver.recv()).await
        {
            if event.is_some() {
                count += 1;
            } else {
                break;
            }
        }
        debug!("Flushed {} events from queue", count);
        count
    }

    /// Waits for a specific type of event
    ///
    /// # Arguments
    /// * `timeout` - Maximum time to wait for the event
    /// * `predicate` - Function to test if an event matches
    ///
    /// # Returns
    /// The matching event if found within timeout
    ///
    /// # Errors
    /// Returns an error if timeout is reached or event reception fails
    pub async fn wait_for_event<F>(&mut self, timeout: Duration, mut predicate: F) -> Result<Event>
    where
        F: FnMut(&Event) -> bool,
    {
        let deadline = tokio::time::Instant::now() + timeout;

        while tokio::time::Instant::now() < deadline {
            match tokio::time::timeout(Duration::from_millis(10), self.next()).await {
                Ok(Ok(event)) => {
                    if predicate(&event) {
                        return Ok(event);
                    }
                    // Event didn't match, continue waiting
                }
                Ok(Err(e)) => return Err(e),
                Err(_) => {
                    // Timeout on individual receive, continue waiting until deadline
                }
            }
        }

        Err(anyhow::anyhow!("Timeout waiting for matching event"))
    }

    /// Processes events in batches for better performance
    ///
    /// # Arguments
    /// * `max_events` - Maximum number of events to process in one batch
    /// * `timeout` - Maximum time to spend collecting events
    ///
    /// # Returns
    /// Vector of events collected in the batch
    ///
    /// # Errors
    /// Returns an error if event processing fails
    pub async fn next_batch(&mut self, max_events: usize, timeout: Duration) -> Result<Vec<Event>> {
        let mut events = Vec::new();
        let deadline = tokio::time::Instant::now() + timeout;

        while events.len() < max_events && tokio::time::Instant::now() < deadline {
            match tokio::time::timeout(Duration::from_millis(1), self.next()).await {
                Ok(Ok(event)) => {
                    events.push(event);
                }
                Ok(Err(e)) => return Err(e),
                Err(_) => {
                    // Timeout - return what we have so far
                    break;
                }
            }
        }

        debug!("Collected {} events in batch", events.len());
        Ok(events)
    }

    /// Gets statistics about event processing
    ///
    /// # Returns
    /// Formatted string with event processing statistics
    #[must_use]
    pub fn statistics(&self) -> String {
        format!(
            "Event Handler Statistics:\n\
             - Queue empty: {}\n\
             - Approximate pending: {}",
            self.receiver.is_empty(),
            self.pending_event_count()
        )
    }
}

impl Drop for EventHandler {
    fn drop(&mut self) {
        debug!("Event handler being dropped, cleaning up resources");
        // The background task will be automatically cancelled when the EventHandler is dropped
    }
}

/// Utility functions for event handling
pub mod utils {
    use super::KeyEvent;
    use crossterm::event::{KeyCode, KeyModifiers};

    /// Checks if a key event represents a quit command
    ///
    /// # Arguments
    /// * `key_event` - The key event to check
    ///
    /// # Returns
    /// True if the key event represents a quit command
    #[must_use]
    pub fn is_quit_event(key_event: &KeyEvent) -> bool {
        matches!(
            (key_event.code, key_event.modifiers),
            (KeyCode::Char('q') | KeyCode::Esc, KeyModifiers::NONE)
                | (KeyCode::Char('c'), KeyModifiers::CONTROL)
        )
    }

    /// Checks if a key event represents a refresh command
    ///
    /// # Arguments
    /// * `key_event` - The key event to check
    ///
    /// # Returns
    /// True if the key event represents a refresh command
    #[must_use]
    pub fn is_refresh_event(key_event: &KeyEvent) -> bool {
        matches!(
            (key_event.code, key_event.modifiers),
            (KeyCode::Char('r') | KeyCode::F(5), KeyModifiers::NONE)
                | (KeyCode::Char('r'), KeyModifiers::CONTROL)
        )
    }

    /// Checks if a key event represents a help command
    ///
    /// # Arguments
    /// * `key_event` - The key event to check
    ///
    /// # Returns
    /// True if the key event represents a help command
    #[must_use]
    pub fn is_help_event(key_event: &KeyEvent) -> bool {
        matches!(
            (key_event.code, key_event.modifiers),
            (KeyCode::F(1) | KeyCode::Char('?' | 'h'), KeyModifiers::NONE)
        )
    }

    /// Converts a key event to a human-readable string
    ///
    /// # Arguments
    /// * `key_event` - The key event to convert
    ///
    /// # Returns
    /// Human-readable representation of the key event
    #[must_use]
    pub fn key_event_to_string(key_event: &KeyEvent) -> String {
        let mut result = String::new();

        if key_event.modifiers.contains(KeyModifiers::CONTROL) {
            result.push_str("Ctrl+");
        }
        if key_event.modifiers.contains(KeyModifiers::ALT) {
            result.push_str("Alt+");
        }
        if key_event.modifiers.contains(KeyModifiers::SHIFT) {
            result.push_str("Shift+");
        }

        match key_event.code {
            KeyCode::Char(c) => result.push(c),
            KeyCode::F(n) => result.push_str(&format!("F{n}")),
            KeyCode::Enter => result.push_str("Enter"),
            KeyCode::Tab => result.push_str("Tab"),
            KeyCode::Esc => result.push_str("Esc"),

            KeyCode::Up => result.push_str("Up"),
            KeyCode::Down => result.push_str("Down"),
            KeyCode::Left => result.push_str("Left"),
            KeyCode::Right => result.push_str("Right"),
            KeyCode::Home => result.push_str("Home"),
            KeyCode::End => result.push_str("End"),
            KeyCode::PageUp => result.push_str("PageUp"),
            KeyCode::PageDown => result.push_str("PageDown"),
            KeyCode::Insert => result.push_str("Insert"),
            KeyCode::Delete => result.push_str("Delete"),
            KeyCode::Backspace => result.push_str("Backspace"),
            _ => result.push_str(&format!("{:?}", key_event.code)),
        }

        result
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use tokio::time::sleep;

    #[tokio::test]
    async fn test_event_handler_creation() {
        let mut handler = EventHandler::new(100);

        // Should be able to receive tick events
        let event = handler.next().await.unwrap();
        assert!(matches!(event, Event::Tick));
    }

    #[tokio::test]
    async fn test_custom_events() {
        let handler = EventHandler::new(1000); // Slow tick rate

        handler.send_custom("test_message".to_string()).unwrap();

        // We should receive the custom event
        // Note: In a real test, you'd need to handle the async nature properly
    }

    #[tokio::test]
    async fn test_quit_event() {
        let handler = EventHandler::new(1000);

        handler.quit().unwrap();

        // The quit event should be sent successfully
    }

    #[tokio::test]
    async fn test_event_batch_processing() {
        let mut handler = EventHandler::new(10); // Fast tick rate

        // Wait a bit to accumulate some tick events
        sleep(Duration::from_millis(50)).await;

        let events = handler
            .next_batch(5, Duration::from_millis(100))
            .await
            .unwrap();
        assert!(!events.is_empty());
    }
}