sql_cli/state/
dispatcher.rs1use 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
10pub trait StateSubscriber {
12 fn on_state_event(&mut self, event: &StateEvent, buffer: &Buffer);
14
15 fn name(&self) -> &str;
17}
18
19#[derive(Debug)]
21pub enum SubscriberCommand {
22 None,
23 ClearSearch,
24 UpdateViewport,
25 RefreshDisplay,
26}
27
28pub struct StateDispatcher {
30 buffer: Weak<RefCell<Buffer>>,
32
33 subscribers: Vec<Box<dyn StateSubscriber>>,
35
36 event_history: Vec<StateEvent>,
38
39 max_history: usize,
41}
42
43impl Default for StateDispatcher {
44 fn default() -> Self {
45 Self::new()
46 }
47}
48
49impl StateDispatcher {
50 #[must_use]
51 pub fn new() -> Self {
52 Self {
53 buffer: Weak::new(),
54 subscribers: Vec::new(),
55 event_history: Vec::new(),
56 max_history: 100,
57 }
58 }
59
60 pub fn set_buffer(&mut self, buffer: Rc<RefCell<Buffer>>) {
62 self.buffer = Rc::downgrade(&buffer);
63 }
64
65 pub fn subscribe(&mut self, subscriber: Box<dyn StateSubscriber>) {
67 info!("StateDispatcher: Adding subscriber: {}", subscriber.name());
68 self.subscribers.push(subscriber);
69 }
70
71 pub fn dispatch(&mut self, event: StateEvent) {
73 debug!("StateDispatcher: Dispatching event: {:?}", event);
74
75 self.event_history.push(event.clone());
77 if self.event_history.len() > self.max_history {
78 self.event_history.remove(0);
79 }
80
81 let buffer_rc = if let Some(b) = self.buffer.upgrade() {
83 b
84 } else {
85 warn!("StateDispatcher: Buffer reference lost!");
86 return;
87 };
88
89 let change = {
91 let buffer = buffer_rc.borrow();
92 buffer.process_event(&event)
93 };
94
95 if !matches!(
97 change,
98 StateChange {
99 mode: None,
100 search_state: None,
101 filter_state: None,
102 fuzzy_filter_state: None,
103 clear_all_searches: false
104 }
105 ) {
106 info!("StateDispatcher: Applying state change: {:?}", change);
107 buffer_rc.borrow_mut().apply_change(change);
108 }
109
110 let buffer = buffer_rc.borrow();
112 for subscriber in &mut self.subscribers {
113 debug!(
114 "StateDispatcher: Notifying subscriber: {}",
115 subscriber.name()
116 );
117 subscriber.on_state_event(&event, &buffer);
118 }
119 }
120
121 pub fn dispatch_mode_change(
123 &mut self,
124 from: crate::buffer::AppMode,
125 to: crate::buffer::AppMode,
126 ) {
127 self.dispatch(StateEvent::ModeChanged { from, to });
128 }
129
130 pub fn dispatch_search_start(
132 &mut self,
133 search_type: crate::ui::state::shadow_state::SearchType,
134 ) {
135 self.dispatch(StateEvent::SearchStarted { search_type });
136 }
137
138 pub fn dispatch_search_end(&mut self, search_type: crate::ui::state::shadow_state::SearchType) {
140 self.dispatch(StateEvent::SearchEnded { search_type });
141 }
142
143 #[must_use]
145 pub fn get_event_history(&self) -> &[StateEvent] {
146 &self.event_history
147 }
148}
149
150pub struct VimSearchSubscriber {
152 active: bool,
153}
154
155impl Default for VimSearchSubscriber {
156 fn default() -> Self {
157 Self::new()
158 }
159}
160
161impl VimSearchSubscriber {
162 #[must_use]
163 pub fn new() -> Self {
164 Self { active: false }
165 }
166}
167
168impl StateSubscriber for VimSearchSubscriber {
169 fn on_state_event(&mut self, event: &StateEvent, buffer: &Buffer) {
170 match event {
171 StateEvent::SearchStarted { search_type } => {
172 if matches!(search_type, crate::ui::state::shadow_state::SearchType::Vim) {
173 info!("VimSearchSubscriber: Activating for vim search");
174 self.active = true;
175 }
176 }
177 StateEvent::SearchEnded { search_type } => {
178 if matches!(search_type, crate::ui::state::shadow_state::SearchType::Vim) {
179 info!("VimSearchSubscriber: Deactivating - search ended");
180 self.active = false;
181 }
182 }
183 StateEvent::ModeChanged { from: _, to } => {
184 if *to == crate::buffer::AppMode::Results && buffer.search_state.pattern.is_empty()
186 {
187 if self.active {
188 info!("VimSearchSubscriber: Deactivating - mode changed to Results with empty search");
189 }
190 self.active = false;
191 }
192 }
193 _ => {}
194 }
195 }
196
197 fn name(&self) -> &'static str {
198 "VimSearchSubscriber"
199 }
200}