diff --git a/src/client.rs b/src/client.rs index c59b1b9..7ed3db4 100644 --- a/src/client.rs +++ b/src/client.rs @@ -1,2 +1,4 @@ +//! A connection from a client's perspective. + pub mod conn; pub mod state; diff --git a/src/client/conn.rs b/src/client/conn.rs index ec92689..d449ba4 100644 --- a/src/client/conn.rs +++ b/src/client/conn.rs @@ -1,3 +1,5 @@ +//! Client-specific connection with a more expressive API. + use std::{future::Future, time::Duration}; use log::debug; @@ -24,13 +26,27 @@ enum ConnCommand { GetState(oneshot::Sender), } +/// Configuration options for a [`ClientConn`]. #[derive(Debug, Clone)] pub struct ClientConnConfig { + /// The domain where the server is hosted. pub domain: String, + /// Whether the client should present itself as a human to the server. + /// + /// This should only be set if the client is directly acting on behalf of a + /// human, similar to the web client. pub human: bool, + /// The size of the [`mpsc::channel`] for communication between + /// [`ClientConn`] and [`ClientConnHandle`]. pub channel_bufsize: usize, + /// Timeout for opening a websocket connection. pub connect_timeout: Duration, + /// Timeout for server replies when sending euphoria commands, i.e. packets + /// implementing [`Command`]. pub command_timeout: Duration, + /// How long to wait in-between sending pings. + /// + /// See also [`ConnConfig::ping_interval`]. pub ping_interval: Duration, } @@ -47,6 +63,12 @@ impl Default for ClientConnConfig { } } +/// A client connection to an euphoria server. +/// +/// This struct is a wrapper around [`Conn`] with a more client-centric API. It +/// tracks the connection state, including room information sent by the server. +/// It also provides [`ClientConnHandle`], which can be used to asynchronously +/// send commands and await their replies. pub struct ClientConn { rx: mpsc::Receiver, tx: mpsc::Sender, @@ -59,20 +81,33 @@ pub struct ClientConn { } impl ClientConn { + /// Retrieve the current [`State`] of the connection. pub fn state(&self) -> &State { &self.state } + /// Create a new handle for this connection. pub fn handle(&self) -> ClientConnHandle { ClientConnHandle { tx: self.tx.clone(), } } + /// Start closing the connection. + /// + /// To finish closing the connection gracefully, continue calling + /// [`Self::recv`] until it returns [`None`]. pub async fn close(&mut self) -> Result<()> { self.conn.close().await } + /// Receive a [`ParsedPacket`] over the connection. + /// + /// This method also maintains the connection by listening and responding to + /// pings as well as managing [`ClientConnHandle`]s. Thus, it must be called + /// regularly. + /// + /// Returns [`None`] if the connection is closed. pub async fn recv(&mut self) -> Result> { loop { self.replies.purge(); @@ -95,6 +130,10 @@ impl ClientConn { } } + /// Send a packet over the connection. + /// + /// A packet id is automatically generated and returned. When the server + /// replies to the packet, it will use this id as its [`ParsedPacket::id`]. pub async fn send(&mut self, data: impl Into) -> Result { // Overkill of universe-heat-death-like proportions self.last_id = self.last_id.wrapping_add(1); @@ -130,6 +169,9 @@ impl ClientConn { } } + /// Connect to a room. + /// + /// See [`Self::connect_with_config`] for more details. pub async fn connect( room: &str, cookies: Option, @@ -137,6 +179,18 @@ impl ClientConn { Self::connect_with_config(room, cookies, &ClientConnConfig::default()).await } + /// Connect to a room with a specific configuration. + /// + /// Cookies to be sent to the server can be specified as a [`HeaderValue`] + /// in the format of a [`Cookie` request header][0]. If the connection + /// attempt was successful, cookies set by the server will be returned + /// alongside the connection itself as one [`HeaderValue`] per [`Set-Cookie` + /// response header][1]. + /// + /// The tasks of cookie parsing and storage are not handled by this library. + /// + /// [0]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cookie + /// [1]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie pub async fn connect_with_config( room: &str, cookies: Option, @@ -190,6 +244,14 @@ impl ClientConn { } } +/// Asynchronous access to a [`ClientConn`]. +/// +/// Handle methods are only processed while [`ClientConn::recv`] is being +/// called. They may return before they were processed by the associated +/// [`ClientConn`], or they may block until processed. Methods are processed in +/// the order they are called. +/// +/// The handle is cheap to clone. #[derive(Debug, Clone)] pub struct ClientConnHandle { tx: mpsc::Sender, diff --git a/src/conn.rs b/src/conn.rs index 28b05f0..513711a 100644 --- a/src/conn.rs +++ b/src/conn.rs @@ -32,7 +32,10 @@ pub enum Side { /// Configuration options for a [`Conn`]. #[derive(Debug, Clone, PartialEq, Eq)] pub struct ConnConfig { - /// How long to wait in-between pings. + /// How long to wait in-between sending pings. + /// + /// This includes both websocket and euphoria pings ([`Ping`] or + /// [`PingEvent`]). pub ping_interval: Duration, } @@ -117,7 +120,10 @@ impl Conn { } } - /// Close the connection gracefully. + /// Start closing the connection. + /// + /// To finish closing the connection gracefully, continue calling + /// [`Self::recv_raw`] or [`Self::recv`] until they return [`None`]. pub async fn close(&mut self) -> Result<()> { self.ws.close(None).await?; Ok(())