1use std::{
7 fmt::{self, Debug, Formatter},
8 sync::mpsc,
9 thread,
10};
11
12use ratatui::crossterm::event::KeyEvent;
13use serde::{
14 de::{self, Deserializer, Visitor},
15 Deserialize,
16};
17
18pub enum AppEvent {
19 Key(KeyEvent),
20 Resize(usize, usize),
21 Quit,
22 OpenDetail,
23 CloseDetail,
24 ClearDetail,
25 OpenUserCommand(usize),
26 CloseUserCommand,
27 ClearUserCommand,
28 OpenRefs,
29 CloseRefs,
30 OpenHelp,
31 CloseHelp,
32 ClearHelp,
33 SelectNewerCommit,
34 SelectOlderCommit,
35 SelectParentCommit,
36 CopyToClipboard { name: String, value: String },
37 ClearStatusLine,
38 UpdateStatusInput(String, Option<u16>, Option<String>),
39 NotifyInfo(String),
40 NotifySuccess(String),
41 NotifyWarn(String),
42 NotifyError(String),
43}
44
45#[derive(Clone)]
46pub struct Sender {
47 tx: mpsc::Sender<AppEvent>,
48}
49
50impl Sender {
51 pub fn send(&self, event: AppEvent) {
52 self.tx.send(event).unwrap();
53 }
54}
55
56impl Debug for Sender {
57 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
58 write!(f, "Sender")
59 }
60}
61
62pub struct Receiver {
63 rx: mpsc::Receiver<AppEvent>,
64}
65
66impl Receiver {
67 pub fn recv(&self) -> AppEvent {
68 self.rx.recv().unwrap()
69 }
70}
71
72pub fn init() -> (Sender, Receiver) {
73 let (tx, rx) = mpsc::channel();
74 let tx = Sender { tx };
75 let rx = Receiver { rx };
76
77 let event_tx = tx.clone();
78 thread::spawn(move || loop {
79 match ratatui::crossterm::event::read() {
80 Ok(e) => match e {
81 ratatui::crossterm::event::Event::Key(key) => {
82 event_tx.send(AppEvent::Key(key));
83 }
84 ratatui::crossterm::event::Event::Resize(w, h) => {
85 event_tx.send(AppEvent::Resize(w as usize, h as usize));
86 }
87 _ => {}
88 },
89 Err(_) => {
90 event_tx.send(AppEvent::Quit);
92 break;
93 }
94 }
95 });
96
97 (tx, rx)
98}
99
100#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
102pub enum UserEvent {
103 ForceQuit,
104 Quit,
105 HelpToggle,
106 Cancel,
107 Close,
108 NavigateUp,
109 NavigateDown,
110 NavigateRight,
111 NavigateLeft,
112 SelectUp,
113 SelectDown,
114 GoToTop,
115 GoToBottom,
116 GoToParent,
117 ScrollUp,
118 ScrollDown,
119 PageUp,
120 PageDown,
121 HalfPageUp,
122 HalfPageDown,
123 SelectTop,
124 SelectMiddle,
125 SelectBottom,
126 GoToNext,
127 GoToPrevious,
128 Confirm,
129 RefListToggle,
130 Search,
131 UserCommandViewToggle(usize),
132 IgnoreCaseToggle,
133 FuzzyToggle,
134 ShortCopy,
135 FullCopy,
136 Unknown,
137}
138
139impl<'de> Deserialize<'de> for UserEvent {
140 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
141 where
142 D: Deserializer<'de>,
143 {
144 struct UserEventVisitor;
145
146 impl<'de> Visitor<'de> for UserEventVisitor {
147 type Value = UserEvent;
148
149 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
150 formatter.write_str("a string representing a user event")
151 }
152
153 fn visit_str<E>(self, value: &str) -> Result<UserEvent, E>
154 where
155 E: de::Error,
156 {
157 if let Some(num_str) = value.strip_prefix("user_command_view_toggle_") {
158 if let Ok(num) = num_str.parse::<usize>() {
159 Ok(UserEvent::UserCommandViewToggle(num))
160 } else {
161 let msg = format!("Invalid user_command_view_toggle_n format: {}", value);
162 Err(de::Error::custom(msg))
163 }
164 } else {
165 match value {
166 "force_quit" => Ok(UserEvent::ForceQuit),
167 "quit" => Ok(UserEvent::Quit),
168 "help_toggle" => Ok(UserEvent::HelpToggle),
169 "cancel" => Ok(UserEvent::Cancel),
170 "close" => Ok(UserEvent::Close),
171 "navigate_up" => Ok(UserEvent::NavigateUp),
172 "navigate_down" => Ok(UserEvent::NavigateDown),
173 "navigate_right" => Ok(UserEvent::NavigateRight),
174 "navigate_left" => Ok(UserEvent::NavigateLeft),
175 "select_up" => Ok(UserEvent::SelectUp),
176 "select_down" => Ok(UserEvent::SelectDown),
177 "go_to_top" => Ok(UserEvent::GoToTop),
178 "go_to_bottom" => Ok(UserEvent::GoToBottom),
179 "go_to_parent" => Ok(UserEvent::GoToParent),
180 "scroll_up" => Ok(UserEvent::ScrollUp),
181 "scroll_down" => Ok(UserEvent::ScrollDown),
182 "page_up" => Ok(UserEvent::PageUp),
183 "page_down" => Ok(UserEvent::PageDown),
184 "half_page_up" => Ok(UserEvent::HalfPageUp),
185 "half_page_down" => Ok(UserEvent::HalfPageDown),
186 "select_top" => Ok(UserEvent::SelectTop),
187 "select_middle" => Ok(UserEvent::SelectMiddle),
188 "select_bottom" => Ok(UserEvent::SelectBottom),
189 "go_to_next" => Ok(UserEvent::GoToNext),
190 "go_to_previous" => Ok(UserEvent::GoToPrevious),
191 "confirm" => Ok(UserEvent::Confirm),
192 "ref_list_toggle" => Ok(UserEvent::RefListToggle),
193 "search" => Ok(UserEvent::Search),
194 "ignore_case_toggle" => Ok(UserEvent::IgnoreCaseToggle),
195 "fuzzy_toggle" => Ok(UserEvent::FuzzyToggle),
196 "short_copy" => Ok(UserEvent::ShortCopy),
197 "full_copy" => Ok(UserEvent::FullCopy),
198 _ => {
199 let msg = format!("Unknown user event: {}", value);
200 Err(de::Error::custom(msg))
201 }
202 }
203 }
204 }
205 }
206
207 deserializer.deserialize_str(UserEventVisitor)
208 }
209}
210
211impl UserEvent {
212 pub fn is_countable(&self) -> bool {
213 matches!(
214 self,
215 UserEvent::NavigateUp
216 | UserEvent::NavigateDown
217 | UserEvent::ScrollUp
218 | UserEvent::ScrollDown
219 | UserEvent::GoToParent
220 | UserEvent::PageUp
221 | UserEvent::PageDown
222 | UserEvent::HalfPageUp
223 | UserEvent::HalfPageDown
224 )
225 }
226}
227
228#[derive(Debug, Clone, Copy, PartialEq, Eq)]
229pub struct UserEventWithCount {
230 pub event: UserEvent,
231 pub count: usize,
232}
233
234impl UserEventWithCount {
235 pub fn new(event: UserEvent, count: usize) -> Self {
236 Self {
237 event,
238 count: if count == 0 { 1 } else { count },
239 }
240 }
241
242 pub fn from_event(event: UserEvent) -> Self {
243 Self::new(event, 1)
244 }
245}
246
247#[cfg(test)]
248mod tests {
249 use super::*;
250
251 #[test]
252 fn test_user_event_with_count_new() {
253 let event = UserEventWithCount::new(UserEvent::NavigateUp, 5);
254 assert_eq!(event.event, UserEvent::NavigateUp);
255 assert_eq!(event.count, 5);
256 }
257
258 #[test]
259 fn test_user_event_with_count_new_zero_count() {
260 let event = UserEventWithCount::new(UserEvent::NavigateDown, 0);
261 assert_eq!(event.event, UserEvent::NavigateDown);
262 assert_eq!(event.count, 1); }
264
265 #[test]
266 fn test_user_event_with_count_from_event() {
267 let event = UserEventWithCount::from_event(UserEvent::NavigateLeft);
268 assert_eq!(event.event, UserEvent::NavigateLeft);
269 assert_eq!(event.count, 1);
270 }
271
272 #[test]
273 fn test_user_event_with_count_equality() {
274 let event1 = UserEventWithCount::new(UserEvent::ScrollUp, 3);
275 let event2 = UserEventWithCount::new(UserEvent::ScrollUp, 3);
276 let event3 = UserEventWithCount::new(UserEvent::ScrollDown, 3);
277
278 assert_eq!(event1, event2);
279 assert_ne!(event1, event3);
280 }
281}