wiki-tui 0.9.2

A simple and easy to use Wikipedia Text User Interface
Documentation
use std::sync::Arc;

use anyhow::{Context, Result};
use tokio::sync::{mpsc, Mutex};
use tracing::warn;
use wiki_tui::{
    action::{Action, ActionPacket, ActionResult},
    app::AppComponent,
    cli::match_cli,
    components::Component,
    config::{load_config, load_theme, Config, Theme},
    event::EventHandler,
    logging::initialize_logging,
    panic_handler::initialize_panic_handler,
    terminal::Tui,
    trace_dbg,
};

#[tokio::main]
async fn main() -> Result<()> {
    let results = match_cli();
    let mut actions = results.actions;

    initialize_logging(results.log_level)?;
    initialize_panic_handler()?;

    let (action_tx, mut action_rx) = mpsc::unbounded_channel();

    let app_component = Arc::new(Mutex::new(AppComponent::default()));
    let mut should_quit = false;

    let config = load_config()
        .context("failed loading the config")
        .unwrap_or_else(|err| {
            warn!("{:?}", err);
            let action = Action::PopupMessage("Information".to_string(), "Something went wrong when trying to load you configuration\nCheck the logs for further information".to_string());
            match actions {
                Some(ref mut action_packet) => action_packet.add_action(action),
                None => actions = Some(ActionPacket::single(action))
            }
            Config::default()
        });

    let theme = load_theme()
        .context("failed loading the theme")
        .unwrap_or_else(|err| {
            warn!("{:?}", err);
            let action = Action::PopupMessage("Information".to_string(), "Something went wrong when trying to load your theme configuration\nCheck the logs for further information".to_string());
            match actions {
                Some(ref mut action_packet) => action_packet.add_action(action),
                None => actions = Some(ActionPacket::single(action))
            }
            Theme::default()
        });

    app_component
        .lock()
        .await
        .init(action_tx.clone(), Arc::new(config), Arc::new(theme))?;

    let mut tui = Tui::new()?;
    tui.enter()?;

    let _action_tx = action_tx.clone();
    let _root = app_component.clone();

    // Event Thread
    tokio::spawn(async move {
        let render_tick = 20;
        let mut event_handler = EventHandler::new(render_tick);
        loop {
            let event = event_handler.next().await;
            if let ActionResult::Consumed(action) = _root.lock().await.handle_events(event) {
                action.send(&_action_tx);
            }
        }
    });

    // Send actions to be run at startup
    if let Some(actions) = actions {
        let _action_tx = action_tx.clone();
        tokio::spawn(async move {
            actions.send(&_action_tx);
        });
    }

    // Main Loop
    loop {
        if let Some(action) = action_rx.recv().await {
            if !matches!(action, Action::RenderTick) {
                trace_dbg!(&action);
            }

            match action {
                Action::RenderTick => {
                    let mut app_component = app_component.lock().await;
                    tui.terminal
                        .draw(|frame| app_component.render(frame, frame.area()))
                        .unwrap();
                }
                Action::Quit => should_quit = true,
                action => match app_component.lock().await.update(action) {
                    ActionResult::Consumed(action) => action.send(&action_tx),
                    ActionResult::Ignored => {}
                },
            }
        }

        if should_quit {
            break;
        }
    }

    tui.exit()?;
    Ok(())
}