sbd-client 0.4.0

simple websocket-based message relay client
Documentation
use sbd_client::*;
use std::collections::{HashMap, VecDeque};

enum ConCmd {
    Close,
    Send(PubKey, Vec<u8>),
}

#[tokio::main(flavor = "multi_thread")]
async fn main() {
    println!("CMD/READY");

    let mut lines = tokio::io::AsyncBufReadExt::lines(
        tokio::io::BufReader::new(tokio::io::stdin()),
    );

    let mut con_map = HashMap::new();

    while let Ok(Some(line)) = lines.next_line().await {
        let mut parts = line.split("/").collect::<VecDeque<_>>();
        if parts.pop_front().unwrap() != "CMD" {
            panic!();
        }
        match parts.pop_front().unwrap() {
            "CONNECT" => {
                let id: usize = parts.pop_front().unwrap().parse().unwrap();
                let (s, r) = tokio::sync::mpsc::unbounded_channel();
                con_map.insert(id, s);
                tokio::task::spawn(spawn_con(
                    id,
                    r,
                    parts.into_iter().map(|s| s.to_string()).collect(),
                ));
            }
            "SEND" => {
                let id: usize = parts.pop_front().unwrap().parse().unwrap();
                let pk = hex::decode(parts.pop_front().unwrap()).unwrap();
                let msg = hex::decode(parts.pop_front().unwrap()).unwrap();
                if let Some(s) = con_map.get(&id) {
                    let _ = s.send(ConCmd::Send(
                        PubKey(std::sync::Arc::new(pk.try_into().unwrap())),
                        msg,
                    ));
                }
            }
            "CLOSE" => {
                let id: usize = parts.pop_front().unwrap().parse().unwrap();
                if let Some(s) = con_map.get(&id) {
                    let _ = s.send(ConCmd::Close);
                }
            }
            oth => panic!("unhandled: {oth}"),
        }
    }
}

async fn connect(addrs: &[String]) -> (SbdClient, PubKey, MsgRecv) {
    for addr in addrs {
        if let Ok(c) = SbdClient::connect_config(
            &format!("ws://{addr}"),
            &DefaultCrypto::default(),
            SbdClientConfig {
                allow_plain_text: true,
                ..Default::default()
            },
        )
        .await
        {
            let pk = c.0.pub_key().clone();
            return (c.0, pk, c.1);
        }
    }
    panic!()
}

async fn spawn_con(
    id: usize,
    mut r: tokio::sync::mpsc::UnboundedReceiver<ConCmd>,
    addrs: Vec<String>,
) {
    let (cli, pk, mut rcv) = connect(addrs.as_slice()).await;
    tokio::task::spawn(async move {
        while let Some(data) = rcv.recv().await {
            println!(
                "CMD/RECV/{id}/{}/{}",
                hex::encode(data.pub_key_ref()),
                hex::encode(data.message()),
            );
        }
        println!("CMD/CLOSE/{id}");
    });
    println!("CMD/CONNECT/{id}/{}", hex::encode(&pk[..]));
    while let Some(cmd) = r.recv().await {
        match cmd {
            ConCmd::Close => break,
            ConCmd::Send(dest, msg) => {
                if cli.send(&dest, &msg).await.is_err() {
                    break;
                }
            }
        }
    }
    cli.close().await;
}