breakmancer 0.9.0

Drop a breakpoint into any shell.
Documentation
//! Abstraction layer over pluggable transports.
//!
//! This uses enums rather than `Box<dyn ...>` because the latter is
//! not particularly compatible with a *set* of related traits. (The
//! generics or associated types turn into a nightmare to abstract over.)

use tcp::{TcpClient, TcpConnectParams, TcpServer, TcpTransport};
use wormhole::{WormholeCaller, WormholeListener, WormholeTransport};

use crate::protocol::ControllerId;

/// The controller's side of the transport, listening and ready for a
/// connection.
pub enum TransportListener {
    Tcp(TcpServer),
    Wormhole(WormholeListener),
}

impl TransportListener {
    /// English string describing the listening action, for logging.
    pub fn describe_listening(&self) -> String {
        match self {
            Self::Tcp(tcp) => tcp.describe_listening(),
            Self::Wormhole(w) => w.describe_listening(),
        }
    }

    /// Wait for a connection.
    pub async fn accept_connection(&mut self) -> Result<Transport, String> {
        match self {
            Self::Tcp(tcp) => tcp.accept_connection().await.map(Transport::Tcp),
            Self::Wormhole(w) => w.accept_connection().await.map(Transport::Wormhole),
        }
    }
}

/// The breakpoint's side of the transport, able to create new
/// outbound connections.
pub enum TransportCaller {
    Tcp(TcpClient),
    Wormhole(WormholeCaller),
}

impl TransportCaller {
    /// Initiate a connection.
    pub async fn new_connection(&self) -> Result<Transport, String> {
        match self {
            Self::Tcp(tcp) => tcp.new_connection().await.map(Transport::Tcp),
            Self::Wormhole(w) => w.new_connection().await.map(Transport::Wormhole),
        }
    }
}

/// An active connection for some transport mechanism.
///
/// Should close itself gracefully when dropped.
#[allow(clippy::large_enum_variant)]
pub enum Transport {
    Tcp(TcpTransport),
    Wormhole(WormholeTransport),
}

impl Transport {
    /// Send a raw message frame.
    pub async fn send_raw(&mut self, raw: &[u8]) -> Result<(), String> {
        match self {
            Self::Tcp(tcp) => tcp.send_raw(raw).await,
            Self::Wormhole(w) => w.send_raw(raw).await,
        }
    }

    /// Receive a raw message frame.
    ///
    /// Ok(None) signals that the connection has closed, although this
    /// may not be returned by all implementations.
    pub async fn receive_raw(&mut self) -> Result<Option<Vec<u8>>, String> {
        match self {
            Self::Tcp(tcp) => tcp.receive_raw().await,
            Self::Wormhole(w) => w.receive_raw().await,
        }
    }
}

/// Connection parameters.
#[derive(Clone, Debug)]
pub enum ConnectMethod {
    Tcp(TcpConnectParams),
    Wormhole,
}

impl ConnectMethod {
    /// Create a listener. Some transports may start network activity at this point.
    pub async fn new_listener(
        &self,
        controller_id: &ControllerId,
    ) -> Result<TransportListener, String> {
        let listener = match self {
            Self::Tcp(params) => TransportListener::Tcp(TcpServer::listen(params).await?),
            Self::Wormhole => TransportListener::Wormhole(WormholeListener::new(controller_id)),
        };
        Ok(listener)
    }

    /// Create a caller. Some transports may start network activity at this point.
    pub fn new_caller(&self, controller_id: &ControllerId) -> TransportCaller {
        match self {
            Self::Tcp(params) => TransportCaller::Tcp(TcpClient::new(&params.callback_addr)),
            Self::Wormhole => TransportCaller::Wormhole(WormholeCaller::new(controller_id)),
        }
    }
}

pub mod tcp;
pub mod wormhole;