1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
use clap::ArgMatches;

use crate::prelude::*;
use service::main::*;

use simplelog::{CombinedLogger, TermLogger, TerminalMode};

use crate::bus::MainBus;
use message::main::{MainRecv, MainShutdown};
use std::time::Duration;

use lifeline::dyn_bus::DynBus;
use tab_api::launch::*;
use tab_websocket::resource::connection::WebsocketResource;

mod bus;
mod env;
mod message;
mod prelude;
mod service;
mod state;

pub fn command_main(args: ArgMatches) -> anyhow::Result<()> {
    let mut runtime = tokio::runtime::Builder::new()
        .threaded_scheduler()
        .enable_io()
        .enable_time()
        .build()
        .unwrap();

    let result = runtime.block_on(async { main_async(args).await });

    runtime.shutdown_timeout(Duration::from_millis(25));

    result?;

    Ok(())
}

async fn main_async(matches: ArgMatches<'_>) -> anyhow::Result<()> {
    CombinedLogger::init(vec![TermLogger::new(
        LevelFilter::Warn,
        simplelog::ConfigBuilder::new()
            .set_time_format("%+".to_owned())
            .build(),
        TerminalMode::Stderr,
    )])
    .unwrap();

    let select_tab = matches.value_of("TAB-NAME");
    let close_tab = matches.value_of("CLOSE-TAB");
    let (mut tx, rx_shutdown, _service) = spawn().await?;
    let completion = matches.is_present("AUTOCOMPLETE-TAB");
    let close_completion = matches.is_present("AUTOCOMPLETE-CLOSE-TAB");
    let shutdown = matches.is_present("SHUTDOWN");

    if shutdown {
        tx.send(MainRecv::GlobalShutdown).await?;
    } else if completion {
        tx.send(MainRecv::AutocompleteTab).await?;
    } else if close_completion {
        tx.send(MainRecv::AutocompleteCloseTab).await?;
    } else if matches.is_present("LIST") {
        tx.send(MainRecv::ListTabs).await?;
    } else if let Some(tab) = select_tab {
        info!("selecting tab: {}", tab);
        tx.send(MainRecv::SelectTab(tab.to_string())).await?;
    } else if let Some(tab) = close_tab {
        tx.send(MainRecv::CloseTab(tab.to_string())).await?;
    } else {
        tx.send(MainRecv::SelectTab("any/".to_string())).await?;
    }

    wait_for_shutdown(rx_shutdown).await;

    Ok(())
}

async fn spawn() -> anyhow::Result<(
    impl Sender<MainRecv>,
    impl Receiver<MainShutdown>,
    MainService,
)> {
    let daemon_file = launch_daemon().await?;
    let ws_url = format!("ws://127.0.0.1:{}/cli", daemon_file.port);

    let bus = MainBus::default();
    bus.capacity::<Request>(128)?;
    bus.capacity::<Response>(256)?;

    let websocket =
        tab_websocket::connect_authorized(ws_url, daemon_file.auth_token.clone()).await?;
    let websocket = WebsocketResource(websocket);
    bus.store_resource(websocket);

    info!("Launching MainService");
    let service = MainService::spawn(&bus)?;

    let tx = bus.tx::<MainRecv>()?;
    let main_shutdown = bus.rx::<MainShutdown>()?;

    Ok((tx, main_shutdown, service))
}

pub fn normalize_name(name: &str) -> String {
    let name = name.to_string().trim().to_string();
    if name.ends_with("/") {
        name
    } else {
        name + "/"
    }
}