p5 0.10.0

A tui client for Pulumi
Documentation
pub mod controller;

pub type Result<T> = color_eyre::Result<T>;

use ratatui::{crossterm::ExecutableCommand, widgets::StatefulWidget};
use tracing_subscriber::prelude::*;

pub async fn run<H, W>(
    handler: H,
    state: H::State,
    init_actions: Vec<H::Action>,
    widget: W,
) -> color_eyre::Result<()>
where
    H: controller::Handler + Send + Sync + Clone + 'static,
    W: StatefulWidget<State = H::State> + Send + Sync + Clone + 'static,
{
    install_tracing();
    install_panic_hook()?;

    let cancel_token = tokio_util::sync::CancellationToken::new();

    let terminal = init_terminal()?;

    let controller =
        controller::Controller::new(handler, state, init_actions, cancel_token.clone());

    tokio::select! {
        _ = controller.run(terminal, widget) => {
            tracing::info!("Run task completed");
        },
        _ = tokio::signal::ctrl_c() => {
            tracing::info!("Ctrl-C received");
            cancel_token.cancel();
        },
        _ = cancel_token.cancelled() => {
            tracing::info!("Cancellation token triggered, shutting down...");
        },
    }

    tracing::debug!("Shutting down...");
    restore_terminal()?;

    Ok(())
}

fn init_terminal() -> color_eyre::Result<ratatui::DefaultTerminal> {
    ratatui::crossterm::terminal::enable_raw_mode()?;
    std::io::stdout().execute(ratatui::crossterm::terminal::EnterAlternateScreen)?;
    let terminal =
        ratatui::Terminal::new(ratatui::backend::CrosstermBackend::new(std::io::stdout()))?;
    Ok(terminal)
}

fn restore_terminal() -> color_eyre::Result<()> {
    std::io::stdout().execute(ratatui::crossterm::terminal::LeaveAlternateScreen)?;
    ratatui::crossterm::terminal::disable_raw_mode()?;
    Ok(())
}

fn install_panic_hook() -> color_eyre::Result<()> {
    color_eyre::config::HookBuilder::default()
        .panic_section("consider reporting the bug on github")
        .install()
}

fn install_tracing() {
    let registry = tracing_subscriber::registry()
        .with(tracing_error::ErrorLayer::default())
        .with(tracing_subscriber::EnvFilter::from_default_env())
        .with(
            tracing_subscriber::fmt::layer()
                .with_thread_ids(false)
                .with_thread_names(false)
                .with_file(true)
                .with_line_number(true)
                .with_target(false)
                .compact(),
        );

    registry.init();
}