leetcode_tui_core/
event.rs1use crossterm::event::KeyEvent;
2use leetcode_tui_db::{DbQuestion, DbTopic};
3use leetcode_tui_shared::RoCell;
4use std::path::PathBuf;
5
6use tokio::sync::{mpsc::UnboundedSender, oneshot};
7
8static TX: RoCell<UnboundedSender<Event>> = RoCell::new();
9
10pub enum Event {
11 Quit,
12 Key(KeyEvent),
13 Render(String),
14 Resume,
15 Suspend,
16 Resize(u16, u16),
17 Topic(DbTopic),
18 Questions(Vec<DbQuestion>),
19 AddQuestions(Vec<DbQuestion>),
20 AdhocQuestion(DbQuestion),
21 QuestionFilter(Option<String>),
22 Popup(Option<String>, Vec<String>),
23 SelectPopup(
24 Option<String>,
25 Vec<String>,
26 tokio::sync::oneshot::Sender<Option<usize>>,
27 ),
28 Input(super::UBStrSender, Option<String>),
29 Open(PathBuf),
30 Error(String),
31 QuestionUpdate,
32 SyncDb,
33 ProgressUpdate(String, u32, u32),
34}
35
36impl Event {
37 #[inline]
38 pub fn init(tx: UnboundedSender<Event>) {
39 TX.init(tx);
40 }
41
42 #[inline]
43 pub fn emit(self) {
44 TX.as_ref().send(self).ok();
45 }
46
47 pub async fn wait<T>(self, rx: oneshot::Receiver<T>) -> T {
48 TX.as_ref().send(self).ok();
49 rx.await.unwrap_or_else(|_| std::process::exit(0))
50 }
51}
52
53#[macro_export]
54macro_rules! emit {
55 (Key($key:expr)) => {
56 $crate::Event::Key($key).emit();
57 };
58 (Render) => {
59 $crate::Event::Render(format!("{}:{}", file!(), line!())).emit();
60 };
61 (Resize($cols:expr, $rows:expr)) => {
62 $crate::Event::Resize($cols, $rows).emit();
63 };
64 (Topic($topic:expr)) => {
65 $crate::Event::Topic($topic).emit();
66 };
67 (Questions($questions:expr)) => {
68 $crate::Event::Questions($questions).emit();
69 };
70 (AddQuestions($questionList:expr)) => {
71 $crate::Event::AddQuestions($questionList).emit();
72 };
73 (AdhocQuestion($question:expr)) => {
74 $crate::Event::AdhocQuestion($question).emit();
75 };
76 (Popup($lines:expr)) => {
77 $crate::Event::Popup(None, $lines).emit();
78 };
79 (Popup($title:expr, $lines:expr)) => {
80 $crate::Event::Popup(Some($title.into()), $lines).emit();
81 };
82 (SelectPopup($a: expr)) => {{
83 let (tx, rx) = tokio::sync::oneshot::channel();
84 $crate::Event::SelectPopup(None, $a, tx).wait(rx)
85 }};
86 (SelectPopup($title:expr, $a: expr)) => {{
87 let (tx, rx) = tokio::sync::oneshot::channel();
88 $crate::Event::SelectPopup(Some($title.into()), $a, tx).wait(rx)
89 }};
90 (Error($e:expr)) => {
91 $crate::Event::Error($e).emit();
92 };
93 (Open($e:expr)) => {
94 $crate::Event::Open($e).emit();
95 };
96 (Input($e:expr)) => {{
97 let (tx, rx) = tokio::sync::mpsc::unbounded_channel();
98 $crate::Event::Input(tx, $e).emit();
99 rx
100 }};
101 (QuestionFilter($e:expr)) => {
102 $crate::Event::QuestionFilter($e).emit();
103 };
104 (ProgressUpdate($title:expr, $progress:expr, $total:expr)) => {
105 $crate::Event::ProgressUpdate($title, $progress, $total).emit();
106 };
107 ($event:ident) => {
108 $crate::Event::$event.emit();
109 };
110}