mobius-cli 0.15.5

The terminal client for a möbius gateway
Documentation
use std::path::PathBuf;

use clap::Parser as _;
use mobius_cli::gateway_accounts::GatewayAccounts;
use mobius_gateway::client::Endpoint;
use mobius_gateway::command::{FrontendCommand, GatewayCli};

#[tokio::main]
async fn main() -> std::result::Result<(), Box<dyn std::error::Error>> {
    let cli = GatewayCli::parse();
    match cli.frontend_command()? {
        Some(FrontendCommand::Init(state_dir)) => initialize_cloudflare(state_dir).await?,
        Some(FrontendCommand::Dashboard(state_dir)) => {
            mobius_cli::frontend::run_gateway_dashboard(state_dir).await?
        }
        Some(FrontendCommand::Provider(state_dir)) => {
            mobius_cli::frontend::run_gateway_provider(state_dir).await?
        }
        None => mobius_gateway::command::run_cli(cli, save_local_client, load_local_client).await?,
    }
    Ok(())
}

async fn initialize_cloudflare(state_dir: PathBuf) -> mobius_gateway::Result<()> {
    let Some(setup) = mobius_cli::frontend::run_cloudflare_setup().await? else {
        return Ok(());
    };
    if state_dir.try_exists()? {
        if !mobius_cli::frontend::confirm_gateway_reinitialize(&state_dir).await? {
            return Ok(());
        }
        mobius_gateway::command::reset_gateway_state(state_dir.clone())?;
    }
    match setup {
        mobius_cli::frontend::CloudflareInit::Quick => {
            mobius_gateway::command::initialize_quick_cloudflare(state_dir.clone())?;
        }
        mobius_cli::frontend::CloudflareInit::Named { hostname, token } => {
            mobius_gateway::command::initialize_named_cloudflare(
                state_dir.clone(),
                hostname,
                token,
            )?;
        }
    }
    mobius_gateway::command::run(
        vec![
            "connect".into(),
            "--state-dir".into(),
            state_dir.into_os_string(),
        ],
        save_local_client,
        load_local_client,
    )
    .await
}

fn save_local_client(endpoint: &Endpoint, token: String) -> mobius_gateway::Result<()> {
    let mut accounts = GatewayAccounts::load()?;
    accounts.add(endpoint, token)?;
    accounts.save()
}

fn load_local_client(endpoint: &Endpoint) -> mobius_gateway::Result<Option<String>> {
    Ok(GatewayAccounts::load()?.token(endpoint).map(str::to_owned))
}

#[cfg(test)]
mod tests {
    use super::*;

    fn parse(arguments: &[&str]) -> GatewayCli {
        GatewayCli::try_parse_from(
            std::iter::once("mobius-gateway").chain(arguments.iter().copied()),
        )
        .expect("parse gateway command")
    }

    #[test]
    fn no_command_and_provider_select_gateway_frontends() {
        let dashboard = parse(&[]);
        assert!(matches!(
            dashboard.frontend_command().expect("dashboard command"),
            Some(FrontendCommand::Dashboard(_))
        ));
        let init = parse(&["init", "--state-dir", "/tmp/gateway"]);
        assert!(matches!(
            init.frontend_command().expect("init command"),
            Some(FrontendCommand::Init(path)) if path == std::path::Path::new("/tmp/gateway")
        ));
        let provider = parse(&["provider", "--state-dir", "/tmp/gateway"]);
        assert!(matches!(
            provider.frontend_command().expect("provider command"),
            Some(FrontendCommand::Provider(path)) if path == std::path::Path::new("/tmp/gateway")
        ));
    }
}