leetcode_tui_core/
event.rs

1use 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}