p5/
lib.rs

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}