opencord 0.2.1

Open-source ephemeral chat servers with automatic shutdown
Documentation
use std::net::SocketAddr;
use std::time::Duration;

use clap::{Parser, Subcommand};

#[derive(Parser)]
#[command(
    name = "opencord",
    about = "Ephemeral chat servers with automatic shutdown"
)]
struct Cli {
    #[command(subcommand)]
    command: Command,
}

#[derive(Subcommand)]
enum Command {
    /// Start an ephemeral chat server
    Server {
        /// Server lifetime (e.g. 2h, 30m, 1h30m)
        #[arg(long, default_value = "2h", value_parser = parse_lifetime)]
        lifetime: Duration,

        /// Address to listen on
        #[arg(long, default_value = "127.0.0.1:8080")]
        addr: SocketAddr,
    },

    /// Connect to a chat server
    Client {
        /// Your display name
        name: String,

        /// Invite link (e.g. ws://127.0.0.1:8080/?code=ABC123)
        invite_link: String,
    },
}

fn parse_lifetime(s: &str) -> Result<Duration, String> {
    opencord::duration::parse_duration(s)
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let cli = Cli::parse();

    match cli.command {
        Command::Server { lifetime, addr } => {
            opencord::server::run(addr, lifetime).await?;
        }
        Command::Client { name, invite_link } => {
            let result = opencord::client::run(&invite_link, &name).await;

            // tokio::io::stdin() spawns a blocking thread that can't be interrupted,
            // so the runtime would hang waiting for it during shutdown. Force exit
            // once the chat session is over.
            let code = if result.is_ok() { 0 } else { 1 };
            std::process::exit(code);
        }
    }

    Ok(())
}