sql_cli/state/
dispatcher.rs

1//! State dispatcher for pub-sub pattern
2
3use crate::buffer::Buffer;
4use crate::state::coordinator::StateCoordinator;
5use crate::state::events::{StateChange, StateEvent};
6use std::cell::RefCell;
7use std::rc::{Rc, Weak};
8use tracing::{debug, info, warn};
9
10/// Trait for components that subscribe to state changes
11pub trait StateSubscriber {
12    /// Handle a state event
13    fn on_state_event(&mut self, event: &StateEvent, buffer: &Buffer);
14
15    /// Get subscriber name for debugging
16    fn name(&self) -> &str;
17}
18
19/// Commands that subscribers can return to be executed
20#[derive(Debug)]
21pub enum SubscriberCommand {
22    None,
23    ClearSearch,
24    UpdateViewport,
25    RefreshDisplay,
26}
27
28/// State dispatcher that coordinates buffer state changes with subscribers
29pub struct StateDispatcher {
30    /// Weak reference to the buffer (to avoid circular references)
31    buffer: Weak<RefCell<Buffer>>,
32
33    /// List of subscribers
34    subscribers: Vec<Box<dyn StateSubscriber>>,
35
36    /// Event history for debugging
37    event_history: Vec<StateEvent>,
38
39    /// Maximum event history size
40    max_history: usize,
41}
42
43impl StateDispatcher {
44    pub fn new() -> Self {
45        Self {
46            buffer: Weak::new(),
47            subscribers: Vec::new(),
48            event_history: Vec::new(),
49            max_history: 100,
50        }
51    }
52
53    /// Set the buffer this dispatcher coordinates
54    pub fn set_buffer(&mut self, buffer: Rc<RefCell<Buffer>>) {
55        self.buffer = Rc::downgrade(&buffer);
56    }
57
58    /// Add a subscriber
59    pub fn subscribe(&mut self, subscriber: Box<dyn StateSubscriber>) {
60        info!("StateDispatcher: Adding subscriber: {}", subscriber.name());
61        self.subscribers.push(subscriber);
62    }
63
64    /// Dispatch a state event
65    pub fn dispatch(&mut self, event: StateEvent) {
66        debug!("StateDispatcher: Dispatching event: {:?}", event);
67
68        // Record event in history
69        self.event_history.push(event.clone());
70        if self.event_history.len() > self.max_history {
71            self.event_history.remove(0);
72        }
73
74        // Get buffer reference
75        let buffer_rc = match self.buffer.upgrade() {
76            Some(b) => b,
77            None => {
78                warn!("StateDispatcher: Buffer reference lost!");
79                return;
80            }
81        };
82
83        // Process event to get state changes
84        let change = {
85            let buffer = buffer_rc.borrow();
86            buffer.process_event(&event)
87        };
88
89        // Apply changes to buffer
90        if !matches!(
91            change,
92            StateChange {
93                mode: None,
94                search_state: None,
95                filter_state: None,
96                fuzzy_filter_state: None,
97                clear_all_searches: false
98            }
99        ) {
100            info!("StateDispatcher: Applying state change: {:?}", change);
101            buffer_rc.borrow_mut().apply_change(change);
102        }
103
104        // Notify all subscribers
105        let buffer = buffer_rc.borrow();
106        for subscriber in &mut self.subscribers {
107            debug!(
108                "StateDispatcher: Notifying subscriber: {}",
109                subscriber.name()
110            );
111            subscriber.on_state_event(&event, &buffer);
112        }
113    }
114
115    /// Dispatch a mode change event
116    pub fn dispatch_mode_change(
117        &mut self,
118        from: crate::buffer::AppMode,
119        to: crate::buffer::AppMode,
120    ) {
121        self.dispatch(StateEvent::ModeChanged { from, to });
122    }
123
124    /// Dispatch a search start event
125    pub fn dispatch_search_start(
126        &mut self,
127        search_type: crate::ui::state::shadow_state::SearchType,
128    ) {
129        self.dispatch(StateEvent::SearchStarted { search_type });
130    }
131
132    /// Dispatch a search end event
133    pub fn dispatch_search_end(&mut self, search_type: crate::ui::state::shadow_state::SearchType) {
134        self.dispatch(StateEvent::SearchEnded { search_type });
135    }
136
137    /// Get event history for debugging
138    pub fn get_event_history(&self) -> &[StateEvent] {
139        &self.event_history
140    }
141}
142
143/// Example subscriber for VimSearchManager
144pub struct VimSearchSubscriber {
145    active: bool,
146}
147
148impl VimSearchSubscriber {
149    pub fn new() -> Self {
150        Self { active: false }
151    }
152}
153
154impl StateSubscriber for VimSearchSubscriber {
155    fn on_state_event(&mut self, event: &StateEvent, buffer: &Buffer) {
156        match event {
157            StateEvent::SearchStarted { search_type } => {
158                if matches!(search_type, crate::ui::state::shadow_state::SearchType::Vim) {
159                    info!("VimSearchSubscriber: Activating for vim search");
160                    self.active = true;
161                }
162            }
163            StateEvent::SearchEnded { search_type } => {
164                if matches!(search_type, crate::ui::state::shadow_state::SearchType::Vim) {
165                    info!("VimSearchSubscriber: Deactivating - search ended");
166                    self.active = false;
167                }
168            }
169            StateEvent::ModeChanged { from: _, to } => {
170                // Check if we should deactivate based on buffer state
171                if *to == crate::buffer::AppMode::Results && buffer.search_state.pattern.is_empty()
172                {
173                    if self.active {
174                        info!("VimSearchSubscriber: Deactivating - mode changed to Results with empty search");
175                    }
176                    self.active = false;
177                }
178            }
179            _ => {}
180        }
181    }
182
183    fn name(&self) -> &str {
184        "VimSearchSubscriber"
185    }
186}