1pub mod controller;
2
3pub type Result<T> = color_eyre::Result<T>;
4
5use ratatui::{crossterm::ExecutableCommand, widgets::StatefulWidget};
6use tracing_subscriber::prelude::*;
7
8pub async fn run<H, W>(handler: H, state: H::State, widget: W) -> color_eyre::Result<()>
9where
10 H: controller::Handler + Send + Sync + Clone + 'static,
11 W: StatefulWidget<State = H::State> + Send + Sync + Clone + 'static,
12{
13 install_tracing();
14 install_panic_hook()?;
15
16 let cancel_token = tokio_util::sync::CancellationToken::new();
17
18 let terminal = init_terminal()?;
19
20 let controller = controller::Controller::new(handler, state, cancel_token.clone());
21
22 tokio::select! {
23 _ = controller.run(terminal, widget) => {
24 tracing::info!("Run task completed");
25 },
26 _ = tokio::signal::ctrl_c() => {
27 tracing::info!("Ctrl-C received");
28 cancel_token.cancel();
29 },
30 _ = cancel_token.cancelled() => {
31 tracing::info!("Cancellation token triggered, shutting down...");
32 },
33 }
34
35 tracing::debug!("Shutting down...");
36 restore_terminal()?;
37
38 Ok(())
39}
40
41fn init_terminal() -> color_eyre::Result<ratatui::DefaultTerminal> {
42 ratatui::crossterm::terminal::enable_raw_mode()?;
43 std::io::stdout().execute(ratatui::crossterm::terminal::EnterAlternateScreen)?;
44 let terminal =
45 ratatui::Terminal::new(ratatui::backend::CrosstermBackend::new(std::io::stdout()))?;
46 Ok(terminal)
47}
48
49fn restore_terminal() -> color_eyre::Result<()> {
50 std::io::stdout().execute(ratatui::crossterm::terminal::LeaveAlternateScreen)?;
51 ratatui::crossterm::terminal::disable_raw_mode()?;
52 Ok(())
53}
54
55fn install_panic_hook() -> color_eyre::Result<()> {
56 color_eyre::config::HookBuilder::default()
57 .panic_section("consider reporting the bug on github")
58 .install()
59}
60
61fn install_tracing() {
62 let registry = tracing_subscriber::registry()
63 .with(tracing_error::ErrorLayer::default())
64 .with(tracing_subscriber::EnvFilter::from_default_env())
65 .with(
66 tracing_subscriber::fmt::layer()
67 .with_thread_ids(false)
68 .with_thread_names(false)
69 .with_file(true)
70 .with_line_number(true)
71 .with_target(false)
72 .compact(),
73 );
74
75 registry.init();
76}