clankerdiff-client 0.3.3

Renderer-independent local and remote diff client
Documentation
use crate::{
    ClientError, ConnectionHeader,
    error::transport,
    protocol::{client::ClientCommand, server::ServerMessage, shared::ProtocolError},
    transport::ClientMessageTransport,
};
use futures_util::{SinkExt, StreamExt};
use gloo_net::websocket::{Message, futures::WebSocket};

pub struct WebSocketTransport {
    socket: Option<WebSocket>,
}

impl WebSocketTransport {
    pub async fn try_connect(url: &str, headers: &[ConnectionHeader]) -> Result<Self, ClientError> {
        if !headers.is_empty() {
            return Err(ClientError::Transport(
                "custom WebSocket headers are unavailable in browser clients".to_owned(),
            ));
        }
        let socket = WebSocket::open(url).map_err(|error| {
            ClientError::Transport(format!(
                "{error}; HTTPS pages require a compatible wss:// URL"
            ))
        })?;
        Ok(Self {
            socket: Some(socket),
        })
    }

    fn socket(&mut self) -> Result<&mut WebSocket, ClientError> {
        self.socket.as_mut().ok_or(ClientError::Disconnected)
    }
}

impl ClientMessageTransport for WebSocketTransport {
    async fn send(&mut self, command: ClientCommand) -> Result<(), ClientError> {
        let message = Message::Text(command.encode()?);
        self.socket()?.send(message).await.map_err(transport)
    }

    async fn recv(&mut self) -> Result<ServerMessage, ClientError> {
        let message = self
            .socket()?
            .next()
            .await
            .ok_or(ClientError::Disconnected)?
            .map_err(transport)?;
        let Message::Text(text) = message else {
            return Err(ProtocolError::MessageType.into());
        };
        Ok(ServerMessage::decode(&text)?)
    }

    async fn close(&mut self) {
        if let Some(socket) = self.socket.take() {
            let _ = socket.close(None, None);
        }
    }
}