Skip to main content

musli_web/
client.rs

1//! The generic asynchronous client implementation.
2//!
3//! This implements the client side of the same websocket protocol which is
4//! implemented by [`web`] for browsers, but built for native asynchronous
5//! runtimes instead of the browser event loop.
6//!
7//! This is specialized over the `T` parameter through modules such as:
8//!
9//! * [`tungstenite029`] for `tokio-tungstenite` `0.29.x`.
10//!
11//! [`tungstenite029`]: crate::tungstenite029
12//! [`web`]: <https://docs.rs/musli-web/latest/musli_web/web/>
13//!
14//! # Overview
15//!
16//! A [`Service`] is a driver which owns the underlying socket. It has to be
17//! driven by calling [`Service::run`], which is typically done in a dedicated
18//! task.
19//!
20//! A [`Handle`] is a cheap and [`Clone`]-able handle to the service which can be
21//! shared and moved freely between tasks. It is used to perform requests, open
22//! channels, and to listen for broadcasts and state changes.
23
24use core::cell::Cell;
25use core::fmt;
26use core::future::Future;
27use core::marker::PhantomData;
28use core::{any, mem};
29
30use alloc::boxed::Box;
31use alloc::string::{String, ToString};
32use alloc::sync::Arc;
33use alloc::vec::Vec;
34
35use std::collections::HashMap;
36use std::collections::hash_map::Entry;
37use std::sync::Mutex;
38use std::sync::atomic::{AtomicBool, AtomicU8, AtomicU32, Ordering};
39
40use bytes::Bytes;
41use rand::prelude::*;
42use rand::rngs::SmallRng;
43use slab::Slab;
44use tokio::sync::{mpsc, oneshot, watch};
45use tokio::time::{Duration, Instant};
46
47use crate::api::{self, ChannelId, DecodeBody, Event, Format, MessageId};
48use crate::format;
49
50/// The initial reconnect timeout.
51const INITIAL_TIMEOUT: Duration = Duration::from_millis(250);
52/// The maximum reconnect timeout.
53const MAX_TIMEOUT: Duration = Duration::from_millis(4000);
54/// The maximum amount of fuzz added to a reconnect timeout.
55const MAX_FUZZ: u64 = 50;
56/// The default seed used for reconnect fuzzing.
57const DEFAULT_SEED: u64 = 0xdeadbeef;
58
59/// An empty request body.
60#[non_exhaustive]
61pub struct EmptyBody;
62
63/// An empty callback.
64#[non_exhaustive]
65pub struct EmptyCallback;
66
67/// A message received over the underlying socket.
68///
69/// NB: The variants are constructed by the transport bindings, such as
70/// [`tungstenite029`], so none are constructed when the generic core is built
71/// on its own.
72///
73/// [`tungstenite029`]: crate::tungstenite029
74#[cfg_attr(not(feature = "tungstenite029"), allow(dead_code))]
75pub(crate) enum Message {
76    /// A text message was received. The protocol is binary only, so receiving
77    /// one is a protocol error.
78    Text,
79    /// A binary message was received.
80    Binary(Bytes),
81    /// A ping message was received.
82    ///
83    /// Implementations are expected to respond with a pong on their own.
84    Ping,
85    /// A pong message was received.
86    Pong,
87    /// A close message was received.
88    Close,
89}
90
91pub(crate) mod sealed_socket {
92    pub trait Sealed {}
93}
94
95pub(crate) trait SocketImpl
96where
97    Self: 'static + Send + Sized + self::sealed_socket::Sealed,
98{
99    #[doc(hidden)]
100    type Error;
101
102    /// Receive the next message.
103    ///
104    /// The returned future must be cancel safe, since it is used in a `select!`
105    /// loop together with the command queue of the service.
106    #[doc(hidden)]
107    fn recv(&mut self) -> impl Future<Output = Option<Result<Message, Self::Error>>> + Send + '_;
108
109    /// Send a binary message and flush it.
110    #[doc(hidden)]
111    fn send(&mut self, data: &[u8]) -> impl Future<Output = Result<(), Self::Error>> + Send + '_;
112
113    /// Close the socket.
114    #[doc(hidden)]
115    fn close(&mut self) -> impl Future<Output = Result<(), Self::Error>> + Send + '_;
116}
117
118pub(crate) mod sealed_client {
119    pub trait Sealed {}
120}
121
122/// Central trait for asynchronous client integration.
123///
124/// Since websocket clients are provided by many different crates, this trait
125/// abstracts over the details of establishing and driving one.
126///
127/// The corresponding modules provide integrations:
128///
129/// * [`tungstenite029`] for `tokio-tungstenite` `0.29.x`.
130///
131/// [`tungstenite029`]: crate::tungstenite029
132pub trait ClientImpl
133where
134    Self: 'static + Copy + Sized + self::sealed_client::Sealed,
135{
136    #[doc(hidden)]
137    type Error: 'static + Send + Sync + core::error::Error;
138
139    #[doc(hidden)]
140    #[allow(private_bounds)]
141    type Socket: SocketImpl<Error = Self::Error>;
142
143    #[doc(hidden)]
144    fn connect(url: &str) -> impl Future<Output = Result<Self::Socket, Self::Error>> + Send;
145}
146
147/// Construct a new [`ServiceBuilder`] which will connect to `url`.
148pub fn connect<T>(url: impl AsRef<str>) -> ServiceBuilder<T, EmptyCallback>
149where
150    T: ClientImpl,
151{
152    ServiceBuilder {
153        url: url.as_ref().to_string(),
154        on_error: EmptyCallback,
155        reconnect: true,
156        seed: DEFAULT_SEED,
157        format: Format::DEFAULT,
158        _marker: PhantomData,
159    }
160}
161
162/// The state of the connection.
163///
164/// A listener for state changes can be set up through [`Handle::state`].
165#[derive(Debug, PartialEq, Eq, Clone, Copy)]
166#[non_exhaustive]
167pub enum State {
168    /// The connection is open.
169    Open,
170    /// The connection is closed.
171    Closed,
172}
173
174impl State {
175    /// Check if the state is open.
176    ///
177    /// # Examples
178    ///
179    /// ```
180    /// use musli_web::client::State;
181    ///
182    /// assert!(State::Open.is_open());
183    /// assert!(!State::Closed.is_open());
184    /// ```
185    #[inline]
186    pub fn is_open(&self) -> bool {
187        matches!(self, Self::Open)
188    }
189}
190
191/// Trait governing how callbacks are called.
192pub trait Callback<I>
193where
194    Self: 'static + Send + Sync,
195{
196    /// Call the callback.
197    fn call(&self, input: I);
198}
199
200impl<I> Callback<I> for EmptyCallback {
201    #[inline]
202    fn call(&self, _: I) {}
203}
204
205impl<F, I> Callback<I> for F
206where
207    F: 'static + Send + Sync + Fn(I),
208{
209    #[inline]
210    fn call(&self, input: I) {
211        self(input)
212    }
213}
214
215/// Error type for the client.
216#[derive(Debug)]
217pub struct Error {
218    kind: ErrorKind,
219}
220
221impl Error {
222    #[inline]
223    const fn new(kind: ErrorKind) -> Self {
224        Self { kind }
225    }
226
227    /// Check if the error is caused by an empty packet.
228    ///
229    /// # Examples
230    ///
231    /// ```
232    /// use musli_web::client::RawPacket;
233    ///
234    /// let packet = RawPacket::empty();
235    /// let e = packet.decode::<u32>().unwrap_err();
236    ///
237    /// assert!(e.is_empty_packet());
238    /// ```
239    #[inline]
240    pub fn is_empty_packet(&self) -> bool {
241        matches!(self.kind, ErrorKind::EmptyPacket)
242    }
243
244    /// Check if the error is caused by the connection not being open.
245    ///
246    /// This is the error which is produced if a request is performed while the
247    /// service is not connected. See [`Handle::wait_until_open`] for how to
248    /// wait until the connection is available.
249    #[inline]
250    pub fn is_not_connected(&self) -> bool {
251        matches!(self.kind, ErrorKind::NotConnected)
252    }
253
254    /// Check if the error is a server error, and if so return the message
255    /// reported by the server.
256    ///
257    /// Server errors are produced by a [`Handler`] which returns an error or
258    /// which indicates that it does not support the request being made.
259    ///
260    /// [`Handler`]: <https://docs.rs/musli-web/latest/musli_web/ws/trait.Handler.html>
261    #[inline]
262    pub fn as_server_error(&self) -> Option<&str> {
263        match &self.kind {
264            ErrorKind::Server(message) => Some(message),
265            _ => None,
266        }
267    }
268
269    /// Format a client error consisting of a message.
270    #[inline]
271    pub fn message(message: impl fmt::Display) -> Self {
272        Self::new(ErrorKind::Message(message.to_string()))
273    }
274
275    #[inline]
276    fn server(message: impl fmt::Display) -> Self {
277        Self::new(ErrorKind::Server(message.to_string()))
278    }
279
280    #[inline]
281    fn transport<E>(error: E) -> Self
282    where
283        E: 'static + Send + Sync + core::error::Error,
284    {
285        Self::new(ErrorKind::Transport(Box::new(error)))
286    }
287
288    #[inline]
289    fn decode_response_header(error: format::Error) -> Self {
290        Self::new(ErrorKind::DecodeResponseHeader(error))
291    }
292
293    #[inline]
294    fn decode_error_message(error: format::Error) -> Self {
295        Self::new(ErrorKind::DecodeErrorMessage(error))
296    }
297
298    #[inline]
299    fn decode_packet(error: format::Error) -> Self {
300        Self::new(ErrorKind::DecodePacket(error))
301    }
302
303    #[inline]
304    fn encoding_header(error: format::Error) -> Self {
305        Self::new(ErrorKind::EncodingHeader(error))
306    }
307
308    #[inline]
309    fn encoding_body(error: format::Error) -> Self {
310        Self::new(ErrorKind::EncodingBody(error))
311    }
312}
313
314#[derive(Debug)]
315enum ErrorKind {
316    EmptyPacket,
317    NotConnected,
318    Message(String),
319    Server(String),
320    Transport(Box<dyn core::error::Error + Send + Sync>),
321    DecodeResponseHeader(format::Error),
322    DecodeErrorMessage(format::Error),
323    DecodePacket(format::Error),
324    EncodingHeader(format::Error),
325    EncodingBody(format::Error),
326}
327
328impl fmt::Display for Error {
329    #[inline]
330    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
331        match &self.kind {
332            ErrorKind::EmptyPacket => write!(f, "Packet is empty"),
333            ErrorKind::NotConnected => write!(f, "Client is not connected"),
334            ErrorKind::Message(message) => write!(f, "{message}"),
335            ErrorKind::Server(message) => write!(f, "Server error: {message}"),
336            ErrorKind::Transport(..) => write!(f, "Error in underlying transport"),
337            ErrorKind::DecodeResponseHeader(..) => {
338                write!(f, "Encoding error when decoding response header")
339            }
340            ErrorKind::DecodeErrorMessage(..) => {
341                write!(f, "Encoding error when decoding error response")
342            }
343            ErrorKind::DecodePacket(..) => write!(f, "Encoding error when decoding packet"),
344            ErrorKind::EncodingHeader(..) => write!(f, "Encoding error when encoding header"),
345            ErrorKind::EncodingBody(..) => write!(f, "Encoding error when encoding body"),
346        }
347    }
348}
349
350impl core::error::Error for Error {
351    #[inline]
352    fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
353        match &self.kind {
354            ErrorKind::Transport(error) => Some(&**error),
355            ErrorKind::DecodeResponseHeader(error) => Some(error),
356            ErrorKind::DecodeErrorMessage(error) => Some(error),
357            ErrorKind::DecodePacket(error) => Some(error),
358            ErrorKind::EncodingHeader(error) => Some(error),
359            ErrorKind::EncodingBody(error) => Some(error),
360            _ => None,
361        }
362    }
363}
364
365type Result<T, E = Error> = core::result::Result<T, E>;
366
367/// Slab of broadcast listeners.
368type Broadcasts = HashMap<MessageId, Slab<mpsc::UnboundedSender<Result<RawPacket>>>>;
369
370/// A command sent from a [`Handle`] to the [`Service`] driving the connection.
371enum Command {
372    /// Send an already encoded message and register a pending response.
373    Send {
374        serial: u32,
375        data: Vec<u8>,
376        pending: Pending,
377    },
378    /// Cleanly disconnect a channel.
379    Disconnect { channel: ChannelId },
380    /// Close the service, causing [`Service::run`] to return.
381    Close,
382}
383
384/// A pending response being waited for.
385enum Pending {
386    /// A format negotiation issued by the service itself.
387    Negotiate { format: Format },
388    /// A regular request, tagged with the endpoint it belongs to.
389    Request {
390        id: MessageId,
391        reply: oneshot::Sender<Result<RawPacket>>,
392    },
393    /// A channel being opened.
394    Channel {
395        reply: oneshot::Sender<Result<ChannelId>>,
396    },
397}
398
399impl Pending {
400    #[inline]
401    fn error(self, error: Error) {
402        match self {
403            Pending::Negotiate { .. } => {
404                tracing::debug!("Format negotiation failed: {error}");
405            }
406            Pending::Request { reply, .. } => {
407                _ = reply.send(Err(error));
408            }
409            Pending::Channel { reply } => {
410                _ = reply.send(Err(error));
411            }
412        }
413    }
414}
415
416/// State shared between a [`Service`] and every [`Handle`] associated with it.
417struct Shared {
418    tx: mpsc::UnboundedSender<Command>,
419    serial: AtomicU32,
420    state: watch::Sender<State>,
421    broadcasts: Mutex<Broadcasts>,
422    /// Set once the service driving this state is gone, at which point no
423    /// further state changes can be observed.
424    gone: AtomicBool,
425    /// The format which is actually in effect, as agreed by the [negotiation
426    /// protocol]. This can differ from the requested format if the server did
427    /// not support it.
428    ///
429    /// [negotiation protocol]: crate::api#negotiating-the-format
430    format: AtomicU8,
431}
432
433impl Shared {
434    #[inline]
435    fn next_serial(&self) -> u32 {
436        self.serial.fetch_add(1, Ordering::Relaxed)
437    }
438
439    #[inline]
440    fn is_open(&self) -> bool {
441        self.state.borrow().is_open()
442    }
443
444    /// The format currently in effect.
445    #[inline]
446    fn format(&self) -> Format {
447        Format::from_u8(self.format.load(Ordering::Acquire)).unwrap_or(Format::DEFAULT)
448    }
449
450    #[inline]
451    fn set_format(&self, format: Format) {
452        self.format.store(format.to_u8(), Ordering::Release);
453    }
454
455    /// Test if the service driving this state is gone.
456    #[inline]
457    fn is_gone(&self) -> bool {
458        self.gone.load(Ordering::Acquire)
459    }
460
461    /// Mark the service as gone and wake up anyone waiting for a state change.
462    fn set_gone(&self) {
463        self.gone.store(true, Ordering::Release);
464        // NB: Unlike `send_if_modified` this always notifies, which is needed
465        // to wake up waiters even if the state itself did not change.
466        self.state.send_modify(|state| *state = State::Closed);
467    }
468
469    #[inline]
470    fn send(&self, command: Command) -> Result<()> {
471        if self.tx.send(command).is_err() {
472            return Err(Error::message("Client service is down"));
473        }
474
475        Ok(())
476    }
477}
478
479/// Builder of a [`Service`].
480///
481/// Constructed through [`connect()`].
482pub struct ServiceBuilder<T, E> {
483    url: String,
484    on_error: E,
485    reconnect: bool,
486    seed: u64,
487    format: Format,
488    _marker: PhantomData<T>,
489}
490
491impl<T, E> ServiceBuilder<T, E>
492where
493    T: ClientImpl,
494    E: Callback<Error>,
495{
496    /// Set the error handler to use for the service.
497    ///
498    /// Errors which are reported here are errors which cannot be associated
499    /// with a particular request, such as a failure to connect or a message
500    /// which could not be decoded.
501    #[inline]
502    pub fn on_error<U>(self, on_error: U) -> ServiceBuilder<T, U>
503    where
504        U: Callback<Error>,
505    {
506        ServiceBuilder {
507            url: self.url,
508            on_error,
509            reconnect: self.reconnect,
510            seed: self.seed,
511            format: self.format,
512            _marker: self._marker,
513        }
514    }
515
516    /// Set the [`Format`] to use for message bodies.
517    ///
518    /// The format is negotiated with the server once the connection is
519    /// established, see the [negotiation protocol]. If the server does not
520    /// support it the error is reported through [`ServiceBuilder::on_error`]
521    /// and the connection falls back to [`Format::DEFAULT`], which can be
522    /// observed through [`Handle::format`].
523    ///
524    /// Defaults to [`Format::DEFAULT`].
525    ///
526    /// [negotiation protocol]: crate::api#negotiating-the-format
527    #[inline]
528    pub fn format(mut self, format: Format) -> Self {
529        self.format = format;
530        self
531    }
532
533    /// Configure whether the service should try to reconnect when the
534    /// connection is lost.
535    ///
536    /// This defaults to `true`. If this is disabled, [`Service::run`] returns
537    /// once the connection has been lost or could not be established.
538    #[inline]
539    pub fn reconnect(mut self, reconnect: bool) -> Self {
540        self.reconnect = reconnect;
541        self
542    }
543
544    /// Associate the specified seed with the service.
545    ///
546    /// This affects the random fuzzing which is applied to reconnect timeouts.
547    ///
548    /// By default the seed is a constant value.
549    #[inline]
550    pub fn seed(mut self, seed: u64) -> Self {
551        self.seed = seed;
552        self
553    }
554
555    /// Build the service.
556    ///
557    /// Note that no connection is established until [`Service::run`] is called.
558    pub fn build(self) -> Service<T> {
559        let (tx, rx) = mpsc::unbounded_channel();
560        let (state, _) = watch::channel(State::Closed);
561
562        let shared = Arc::new(Shared {
563            tx,
564            serial: AtomicU32::new(0),
565            state,
566            broadcasts: Mutex::new(Broadcasts::new()),
567            gone: AtomicBool::new(false),
568            format: AtomicU8::new(self.format.to_u8()),
569        });
570
571        Service {
572            handle: Handle {
573                shared: shared.clone(),
574            },
575            shared,
576            rx,
577            url: self.url,
578            on_error: Box::new(self.on_error),
579            reconnect: self.reconnect,
580            socket: None,
581            pending: HashMap::new(),
582            timeout: INITIAL_TIMEOUT,
583            next_attempt: Some(Instant::now()),
584            rng: SmallRng::seed_from_u64(self.seed),
585            closed: false,
586            requested: self.format,
587        }
588    }
589}
590
591/// The service which drives a connection.
592///
593/// This is constructed through [`connect()`] and has to be driven by calling
594/// [`Service::run`].
595pub struct Service<T>
596where
597    T: ClientImpl,
598{
599    handle: Handle,
600    shared: Arc<Shared>,
601    rx: mpsc::UnboundedReceiver<Command>,
602    url: String,
603    on_error: Box<dyn Callback<Error>>,
604    reconnect: bool,
605    socket: Option<T::Socket>,
606    pending: HashMap<u32, Pending>,
607    timeout: Duration,
608    next_attempt: Option<Instant>,
609    rng: SmallRng,
610    closed: bool,
611    /// The format the user asked for, which is re-negotiated on every
612    /// reconnect.
613    requested: Format,
614}
615
616/// The event produced by one iteration of the [`Service::run`] loop.
617enum Output<E> {
618    /// A message was received over the socket.
619    Message(Option<Result<Message, E>>),
620    /// A command was received from a handle.
621    Command(Option<Command>),
622    /// It is time to try and establish a connection.
623    Connect,
624}
625
626impl<T> Service<T>
627where
628    T: ClientImpl,
629{
630    /// Get a handle to the service.
631    ///
632    /// The returned handle can be cloned and moved freely between tasks.
633    #[inline]
634    pub fn handle(&self) -> &Handle {
635        &self.handle
636    }
637
638    /// Run the service.
639    ///
640    /// This drives the underlying connection and must be called for any
641    /// requests to be processed. It is typically spawned onto a task of its
642    /// own.
643    ///
644    /// Unless disabled through [`ServiceBuilder::reconnect`], a connection
645    /// which is lost is re-established with an exponential backoff. Any
646    /// requests which were in flight at that point are failed.
647    ///
648    /// This returns once [`Handle::close`] has been called, or the connection
649    /// has been lost while reconnecting is disabled.
650    pub async fn run(&mut self) -> Result<()> {
651        while !self.closed {
652            let output = {
653                let rx = &mut self.rx;
654
655                match &mut self.socket {
656                    Some(socket) => {
657                        tokio::select! {
658                            message = socket.recv() => Output::Message(message),
659                            command = rx.recv() => Output::Command(command),
660                        }
661                    }
662                    None => match self.next_attempt {
663                        Some(deadline) => {
664                            tokio::select! {
665                                _ = tokio::time::sleep_until(deadline) => Output::Connect,
666                                command = rx.recv() => Output::Command(command),
667                            }
668                        }
669                        None => Output::Command(rx.recv().await),
670                    },
671                }
672            };
673
674            match output {
675                Output::Connect => {
676                    self.connect().await;
677                }
678                Output::Command(command) => {
679                    let Some(command) = command else {
680                        // Every handle has been dropped and no more commands
681                        // can be received.
682                        break;
683                    };
684
685                    self.command(command).await;
686                }
687                Output::Message(message) => {
688                    let Some(message) = message else {
689                        tracing::debug!("Connection closed by server");
690                        self.disconnect().await;
691                        continue;
692                    };
693
694                    let message = match message {
695                        Ok(message) => message,
696                        Err(error) => {
697                            self.on_error.call(Error::transport(error));
698                            self.disconnect().await;
699                            continue;
700                        }
701                    };
702
703                    match message {
704                        Message::Binary(bytes) => match self.message(bytes) {
705                            Ok(Post::Negotiate) => self.send_negotiate().await,
706                            Ok(Post::None) => {}
707                            Err(error) => self.on_error.call(error),
708                        },
709                        Message::Text => {
710                            self.on_error
711                                .call(Error::message("Unsupported text message"));
712                            self.disconnect().await;
713                        }
714                        Message::Ping | Message::Pong => {}
715                        Message::Close => {
716                            tracing::debug!("Close message received");
717                            self.disconnect().await;
718                        }
719                    }
720                }
721            }
722        }
723
724        self.shutdown().await;
725        Ok(())
726    }
727
728    /// Try to establish a connection.
729    async fn connect(&mut self) {
730        tracing::debug!(url = self.url.as_str(), "Connecting");
731
732        match T::connect(&self.url).await {
733            Ok(socket) => {
734                tracing::debug!("Connection established");
735                self.socket = Some(socket);
736                self.next_attempt = None;
737                self.timeout = INITIAL_TIMEOUT;
738            }
739            Err(error) => {
740                self.on_error.call(Error::transport(error));
741                self.schedule_reconnect();
742            }
743        }
744    }
745
746    /// Tear down the current connection and schedule a reconnect.
747    async fn disconnect(&mut self) {
748        if let Some(mut socket) = self.socket.take() {
749            _ = socket.close().await;
750        }
751
752        self.emit_state(State::Closed);
753        self.close_pending(|| Error::message("Connection closed"));
754        self.schedule_reconnect();
755    }
756
757    /// Shut the service down for good.
758    async fn shutdown(&mut self) {
759        if let Some(mut socket) = self.socket.take() {
760            _ = socket.close().await;
761        }
762
763        self.emit_state(State::Closed);
764        self.close_pending(|| Error::message("Client service closed"));
765    }
766
767    fn schedule_reconnect(&mut self) {
768        if !self.reconnect {
769            tracing::debug!("Reconnecting is disabled, closing service");
770            self.closed = true;
771            return;
772        }
773
774        let fuzz = self.rng.random_range(0..=MAX_FUZZ);
775
776        let timeout = self
777            .timeout
778            .saturating_add(Duration::from_millis(fuzz))
779            .min(MAX_TIMEOUT);
780
781        self.timeout = self.timeout.saturating_mul(2).min(MAX_TIMEOUT);
782        self.next_attempt = Some(Instant::now() + timeout);
783        tracing::debug!(?timeout, "Scheduling reconnect");
784    }
785
786    /// Fail every pending request, since there is no chance they will be
787    /// responded to any more.
788    fn close_pending(&mut self, error: impl Fn() -> Error) {
789        for (_, pending) in self.pending.drain() {
790            pending.error(error());
791        }
792    }
793
794    fn emit_state(&mut self, state: State) {
795        self.shared.state.send_if_modified(|current| {
796            if *current == state {
797                return false;
798            }
799
800            *current = state;
801            true
802        });
803    }
804
805    /// Handle a command received from a handle.
806    async fn command(&mut self, command: Command) {
807        match command {
808            Command::Send {
809                serial,
810                data,
811                pending,
812            } => {
813                let Some(socket) = self.socket.as_mut() else {
814                    pending.error(Error::new(ErrorKind::NotConnected));
815                    return;
816                };
817
818                if let Err(error) = socket.send(&data).await {
819                    pending.error(Error::transport(error));
820                    self.disconnect().await;
821                    return;
822                }
823
824                if let Some(existing) = self.pending.insert(serial, pending) {
825                    existing.error(Error::message("Request cancelled"));
826                }
827            }
828            Command::Disconnect { channel } => {
829                if let Err(error) = self.send_disconnect(channel).await {
830                    self.on_error.call(error);
831                }
832            }
833            Command::Close => {
834                self.closed = true;
835            }
836        }
837    }
838
839    async fn send_disconnect(&mut self, channel: ChannelId) -> Result<()> {
840        let Some(socket) = self.socket.as_mut() else {
841            return Ok(());
842        };
843
844        let mut data = Vec::new();
845
846        let header = api::RequestHeader {
847            serial: 0,
848            id: MessageId::DISCONNECT.get(),
849            // NB: Carries no body.
850            format: 0,
851            channel,
852        };
853
854        format::encode_envelope(&mut data, &header).map_err(Error::encoding_header)?;
855
856        tracing::debug!(?channel, "Sending disconnect");
857
858        if let Err(error) = socket.send(&data).await {
859            let error = Error::transport(error);
860            self.disconnect().await;
861            return Err(error);
862        }
863
864        Ok(())
865    }
866
867    /// Dispatch a value to every listener of the given broadcast.
868    ///
869    /// Note that listeners are never removed here, since removal is the
870    /// exclusive responsibility of the corresponding [`Listener`]. Sending to a
871    /// listener which has been dropped but not yet cleared is simply ignored.
872    fn dispatch(&self, id: MessageId, value: impl Fn() -> Result<RawPacket>) {
873        let broadcasts = self
874            .shared
875            .broadcasts
876            .lock()
877            .unwrap_or_else(|e| e.into_inner());
878
879        let Some(slots) = broadcasts.get(&id) else {
880            return;
881        };
882
883        for (_, tx) in slots.iter() {
884            _ = tx.send(value());
885        }
886    }
887
888    /// Resolve the format a message body is encoded with from its envelope.
889    fn body_format(header: &api::ResponseHeader) -> Result<Format> {
890        let Some(format) = Format::from_u8(header.format) else {
891            return Err(Error::message(format_args!(
892                "Server used unknown format id {} for a message body",
893                header.format
894            )));
895        };
896
897        Ok(format)
898    }
899
900    /// Process an incoming binary message.
901    fn message(&mut self, bytes: Bytes) -> Result<Post> {
902        let mut at = 0;
903
904        let header: api::ResponseHeader =
905            format::decode_envelope(&bytes, &mut at).map_err(Error::decode_response_header)?;
906
907        if let Some(broadcast) = MessageId::new(header.broadcast) {
908            tracing::debug!(?header, "Got broadcast");
909
910            if broadcast == MessageId::SERVER_HELLO {
911                // NB: The connection is not reported as open until the format
912                // has been negotiated, so that server-initiated messages are
913                // never encoded with a format this client did not agree to.
914                tracing::debug!("Server hello, negotiating format");
915                return Ok(Post::Negotiate);
916            }
917
918            if let Some(id) = MessageId::new(header.error) {
919                let error = match id {
920                    MessageId::ERROR_MESSAGE => Self::body_format(&header)?
921                        .decode(&bytes, &mut at)
922                        .map_err(Error::decode_error_message)?,
923                    _ => api::ErrorMessage {
924                        message: "Unsupported broadcast",
925                    },
926                };
927
928                self.dispatch(broadcast, || Err(Error::server(error.message)));
929                return Ok(Post::None);
930            }
931
932            let format = Self::body_format(&header)?;
933
934            let packet = RawPacket {
935                id: broadcast,
936                buf: bytes,
937                at: Cell::new(at),
938                format,
939                channel: header.channel,
940            };
941
942            self.dispatch(broadcast, || Ok(packet.clone()));
943            return Ok(Post::None);
944        }
945
946        tracing::debug!(?header, "Got response");
947
948        let Some(pending) = self.pending.remove(&header.serial) else {
949            // NB: This is normal, it simply indicates that the request has been
950            // cancelled.
951            tracing::trace!(?header.serial, "Got message with unknown serial");
952            return Ok(Post::None);
953        };
954
955        if let Some(id) = MessageId::new(header.error) {
956            let error = match id {
957                MessageId::ERROR_MESSAGE => Self::body_format(&header)?
958                    .decode(&bytes, &mut at)
959                    .map_err(Error::decode_error_message)?,
960                _ => api::ErrorMessage {
961                    message: "Unsupported request",
962                },
963            };
964
965            match pending {
966                Pending::Negotiate { format } => {
967                    // NB: The server cannot speak the requested format, so fall
968                    // back to the default rather than leaving the connection
969                    // unusable. The effective format is observable through
970                    // `Handle::format`.
971                    self.on_error.call(Error::message(format_args!(
972                        "Server rejected format `{format}` ({}), falling back to `{}`",
973                        error.message,
974                        Format::DEFAULT
975                    )));
976
977                    self.shared.set_format(Format::DEFAULT);
978                    self.emit_state(State::Open);
979                }
980                pending => {
981                    pending.error(Error::server(error.message));
982                }
983            }
984
985            return Ok(Post::None);
986        }
987
988        match pending {
989            Pending::Negotiate { format } => {
990                // NB: Trust the format the server echoed back over the one that
991                // was asked for, so that a server which downgrades is honored.
992                let accepted = Format::from_u8(header.format).unwrap_or(format);
993                tracing::debug!(?accepted, "Format negotiated");
994                self.shared.set_format(accepted);
995                self.emit_state(State::Open);
996            }
997            Pending::Channel { reply } => {
998                _ = reply.send(Ok(header.channel));
999            }
1000            Pending::Request { id, reply } => {
1001                let format = Self::body_format(&header)?;
1002
1003                let packet = RawPacket {
1004                    id,
1005                    buf: bytes,
1006                    at: Cell::new(at),
1007                    format,
1008                    channel: header.channel,
1009                };
1010
1011                _ = reply.send(Ok(packet));
1012            }
1013        }
1014
1015        Ok(Post::None)
1016    }
1017
1018    /// Ask the server to use the requested format for the rest of the
1019    /// connection.
1020    async fn send_negotiate(&mut self) {
1021        let format = self.requested;
1022        let serial = self.shared.next_serial();
1023
1024        let header = api::RequestHeader {
1025            serial,
1026            id: MessageId::NEGOTIATE.get(),
1027            format: format.to_u8(),
1028            // NB: Carries no body.
1029            channel: ChannelId::NONE,
1030        };
1031
1032        let mut data = Vec::new();
1033
1034        if let Err(error) = format::encode_envelope(&mut data, &header) {
1035            self.on_error.call(Error::encoding_header(error));
1036            return;
1037        }
1038
1039        let Some(socket) = self.socket.as_mut() else {
1040            return;
1041        };
1042
1043        tracing::debug!(?format, "Requesting format");
1044
1045        if let Err(error) = socket.send(&data).await {
1046            self.on_error.call(Error::transport(error));
1047            self.disconnect().await;
1048            return;
1049        }
1050
1051        self.pending.insert(serial, Pending::Negotiate { format });
1052    }
1053}
1054
1055/// Work which has to happen after a message has been processed, once the
1056/// borrow of the message buffer has been released.
1057enum Post {
1058    /// Nothing to do.
1059    None,
1060    /// The format has to be negotiated with the server.
1061    Negotiate,
1062}
1063
1064impl<T> Drop for Service<T>
1065where
1066    T: ClientImpl,
1067{
1068    fn drop(&mut self) {
1069        self.shared.set_gone();
1070
1071        for (_, pending) in self.pending.drain() {
1072            pending.error(Error::message("Client service closed"));
1073        }
1074
1075        self.shared
1076            .broadcasts
1077            .lock()
1078            .unwrap_or_else(|e| e.into_inner())
1079            .clear();
1080    }
1081}
1082
1083impl<T> fmt::Debug for Service<T>
1084where
1085    T: ClientImpl,
1086{
1087    #[inline]
1088    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1089        f.debug_struct("Service")
1090            .field("url", &self.url)
1091            .field("state", &*self.shared.state.borrow())
1092            .finish_non_exhaustive()
1093    }
1094}
1095
1096/// A handle to the client service.
1097///
1098/// This is cheap to clone and can be moved freely between tasks.
1099#[derive(Clone)]
1100pub struct Handle {
1101    shared: Arc<Shared>,
1102}
1103
1104impl Handle {
1105    /// Get the current state of the connection.
1106    #[inline]
1107    pub fn state(&self) -> State {
1108        *self.shared.state.borrow()
1109    }
1110
1111    /// Check if the connection is currently open.
1112    #[inline]
1113    pub fn is_open(&self) -> bool {
1114        self.shared.is_open()
1115    }
1116
1117    /// The [`Format`] currently in effect for message bodies.
1118    ///
1119    /// Before the connection has been opened this is the format which was
1120    /// requested through [`ServiceBuilder::format`]. Once the connection is
1121    /// open it is the format the server actually agreed to, which can differ if
1122    /// the server does not support what was asked for.
1123    #[inline]
1124    pub fn format(&self) -> Format {
1125        self.shared.format()
1126    }
1127
1128    /// Listen for state changes to the underlying connection.
1129    ///
1130    /// This indicates when the connection is open and ready to receive requests
1131    /// through [`State::Open`], or if it's closed and requests will be rejected
1132    /// through [`State::Closed`].
1133    #[inline]
1134    pub fn on_state_change(&self) -> StateListener {
1135        StateListener {
1136            rx: self.shared.state.subscribe(),
1137            shared: self.shared.clone(),
1138        }
1139    }
1140
1141    /// Wait until the connection is open.
1142    ///
1143    /// Since a connection is established asynchronously, this must be called
1144    /// before performing the first request unless you are prepared to handle
1145    /// the [`Error::is_not_connected`] error.
1146    ///
1147    /// This errors if the [`Service`] driving the connection is gone, since the
1148    /// connection can then never be established.
1149    pub async fn wait_until_open(&self) -> Result<()> {
1150        let mut listener = self.on_state_change();
1151
1152        if listener.wait_until(State::Open).await {
1153            return Ok(());
1154        }
1155
1156        Err(Error::message("Client service is down"))
1157    }
1158
1159    /// Open a new logical channel to the websocket server.
1160    ///
1161    /// A channel can be uniquely identified on the client and server side over
1162    /// a single connection. This means that if you send a request over a
1163    /// channel using [`Channel::request`], the server can access the
1164    /// [`ChannelId`] to determine which channel sent the request and the client
1165    /// has the ability to correlate any responses sent by the server by
1166    /// inspecting [`Packet::channel`] or [`RawPacket::channel`].
1167    ///
1168    /// The maximum number of channels is implementation defined, but expect it
1169    /// to be relatively low like `65535` (non-zero 16 bits) to reduce payload
1170    /// sizes. Failure to allocate a channel is an error.
1171    ///
1172    /// Note that a channel is scoped to the connection it was opened over. If
1173    /// the connection is lost and re-established, any channel opened over the
1174    /// old connection is stale and has to be opened again.
1175    pub async fn channel(&self) -> Result<Channel> {
1176        if !self.shared.is_open() {
1177            return Err(Error::new(ErrorKind::NotConnected));
1178        }
1179
1180        let serial = self.shared.next_serial();
1181
1182        let header = api::RequestHeader {
1183            serial,
1184            id: MessageId::CONNECT.get(),
1185            // NB: Carries no body.
1186            format: 0,
1187            channel: ChannelId::NONE,
1188        };
1189
1190        let mut data = Vec::new();
1191        format::encode_envelope(&mut data, &header).map_err(Error::encoding_header)?;
1192
1193        let (reply, rx) = oneshot::channel();
1194
1195        self.shared.send(Command::Send {
1196            serial,
1197            data,
1198            pending: Pending::Channel { reply },
1199        })?;
1200
1201        let Ok(result) = rx.await else {
1202            return Err(Error::message("Client service is down"));
1203        };
1204
1205        Ok(Channel {
1206            shared: self.shared.clone(),
1207            id: result?,
1208        })
1209    }
1210
1211    /// Send a request over the default channel.
1212    ///
1213    /// See [`RequestBuilder::send`] for how the request is completed.
1214    #[inline]
1215    pub fn request(&self) -> RequestBuilder<'_, EmptyBody> {
1216        RequestBuilder {
1217            shared: &self.shared,
1218            channel: ChannelId::NONE,
1219            body: EmptyBody,
1220        }
1221    }
1222
1223    /// Listen for broadcasts of type `T`.
1224    ///
1225    /// Broadcasts are buffered in the returned listener until they are received
1226    /// with [`Listener::recv`]. Dropping the listener removes it.
1227    ///
1228    /// Note that the buffer is unbounded, so a listener which is not drained
1229    /// keeps accumulating broadcasts. Drop or [`clear`] a listener which is no
1230    /// longer of interest.
1231    ///
1232    /// [`clear`]: Listener::clear
1233    pub fn on_broadcast<T>(&self) -> Listener<T>
1234    where
1235        T: api::Broadcast,
1236    {
1237        let (tx, rx) = mpsc::unbounded_channel();
1238
1239        let index = {
1240            let mut broadcasts = self
1241                .shared
1242                .broadcasts
1243                .lock()
1244                .unwrap_or_else(|e| e.into_inner());
1245
1246            broadcasts.entry(T::ID).or_default().insert(tx)
1247        };
1248
1249        Listener {
1250            shared: Some(self.shared.clone()),
1251            id: T::ID,
1252            index,
1253            rx,
1254            _marker: PhantomData,
1255        }
1256    }
1257
1258    /// Close the service.
1259    ///
1260    /// This causes [`Service::run`] to return.
1261    #[inline]
1262    pub fn close(&self) {
1263        _ = self.shared.tx.send(Command::Close);
1264    }
1265}
1266
1267impl PartialEq for Handle {
1268    #[inline]
1269    fn eq(&self, other: &Self) -> bool {
1270        Arc::ptr_eq(&self.shared, &other.shared)
1271    }
1272}
1273
1274impl Eq for Handle {}
1275
1276impl fmt::Debug for Handle {
1277    #[inline]
1278    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1279        f.debug_struct("Handle")
1280            .field("state", &*self.shared.state.borrow())
1281            .finish_non_exhaustive()
1282    }
1283}
1284
1285/// A channel to a websocket server.
1286///
1287/// See [`Handle::channel`] for more details.
1288///
1289/// Dropping this cleanly disconnects the channel, which is signalled to the
1290/// server through [`Handler::close_channel`].
1291///
1292/// [`Handler::close_channel`]: <https://docs.rs/musli-web/latest/musli_web/ws/trait.Handler.html#method.close_channel>
1293pub struct Channel {
1294    shared: Arc<Shared>,
1295    id: ChannelId,
1296}
1297
1298impl Channel {
1299    /// Get the channel identifier for this channel.
1300    #[inline]
1301    pub fn id(&self) -> ChannelId {
1302        self.id
1303    }
1304
1305    /// Get a handle associated with this channel.
1306    ///
1307    /// A handle sheds the channel information and allows for setting up things
1308    /// like broadcast listeners.
1309    #[inline]
1310    pub fn handle(&self) -> Handle {
1311        Handle {
1312            shared: self.shared.clone(),
1313        }
1314    }
1315
1316    /// Send a request over the current channel.
1317    ///
1318    /// See [`RequestBuilder::send`] for how the request is completed.
1319    #[inline]
1320    pub fn request(&self) -> RequestBuilder<'_, EmptyBody> {
1321        RequestBuilder {
1322            shared: &self.shared,
1323            channel: self.id,
1324            body: EmptyBody,
1325        }
1326    }
1327}
1328
1329impl Drop for Channel {
1330    #[inline]
1331    fn drop(&mut self) {
1332        _ = self.shared.send(Command::Disconnect { channel: self.id });
1333    }
1334}
1335
1336impl fmt::Debug for Channel {
1337    #[inline]
1338    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1339        f.debug_struct("Channel")
1340            .field("id", &self.id)
1341            .field("state", &*self.shared.state.borrow())
1342            .finish_non_exhaustive()
1343    }
1344}
1345
1346/// A request builder.
1347///
1348/// Set the body of the request with [`RequestBuilder::body`] and send it with
1349/// [`RequestBuilder::send`].
1350pub struct RequestBuilder<'a, B> {
1351    shared: &'a Arc<Shared>,
1352    channel: ChannelId,
1353    body: B,
1354}
1355
1356impl<'a, B> RequestBuilder<'a, B> {
1357    /// Set the body of the request.
1358    #[inline]
1359    pub fn body<U>(self, body: U) -> RequestBuilder<'a, U>
1360    where
1361        U: api::Request,
1362    {
1363        RequestBuilder {
1364            shared: self.shared,
1365            channel: self.channel,
1366            body,
1367        }
1368    }
1369}
1370
1371impl<B> RequestBuilder<'_, B>
1372where
1373    B: api::Request,
1374{
1375    /// Send the request and wait for the typed response.
1376    pub async fn send(self) -> Result<Packet<B::Endpoint>> {
1377        Ok(Packet::new(self.send_raw().await?))
1378    }
1379
1380    /// Send the request and wait for the raw response.
1381    pub async fn send_raw(self) -> Result<RawPacket> {
1382        let id = <B::Endpoint as api::Endpoint>::ID;
1383
1384        if !self.shared.is_open() {
1385            return Err(Error::new(ErrorKind::NotConnected));
1386        }
1387
1388        let serial = self.shared.next_serial();
1389        let format = self.shared.format();
1390
1391        let header = api::RequestHeader {
1392            serial,
1393            id: id.get(),
1394            format: format.to_u8(),
1395            channel: self.channel,
1396        };
1397
1398        let mut data = Vec::new();
1399        format::encode_envelope(&mut data, &header).map_err(Error::encoding_header)?;
1400        format
1401            .encode(&mut data, &self.body)
1402            .map_err(Error::encoding_body)?;
1403
1404        tracing::debug!(serial, ?id, ?format, len = data.len(), "Sending request");
1405
1406        let (reply, rx) = oneshot::channel();
1407
1408        self.shared.send(Command::Send {
1409            serial,
1410            data,
1411            pending: Pending::Request { id, reply },
1412        })?;
1413
1414        let Ok(result) = rx.await else {
1415            return Err(Error::message("Client service is down"));
1416        };
1417
1418        result
1419    }
1420}
1421
1422/// A listener for broadcasts of type `T`.
1423///
1424/// Constructed through [`Handle::on_broadcast`]. Dropping this removes the
1425/// listener.
1426pub struct Listener<T> {
1427    shared: Option<Arc<Shared>>,
1428    id: MessageId,
1429    index: usize,
1430    rx: mpsc::UnboundedReceiver<Result<RawPacket>>,
1431    _marker: PhantomData<T>,
1432}
1433
1434impl<T> Listener<T> {
1435    /// Receive the next raw broadcast.
1436    ///
1437    /// Returns `None` if the service has been shut down.
1438    #[inline]
1439    pub async fn recv_raw(&mut self) -> Option<Result<RawPacket>> {
1440        self.rx.recv().await
1441    }
1442
1443    /// Receive the next broadcast.
1444    ///
1445    /// Returns `None` if the service has been shut down.
1446    #[inline]
1447    pub async fn recv(&mut self) -> Option<Result<Packet<T>>> {
1448        Some(match self.rx.recv().await? {
1449            Ok(packet) => Ok(Packet::new(packet)),
1450            Err(error) => Err(error),
1451        })
1452    }
1453
1454    /// Clear the listener without dropping it.
1455    ///
1456    /// This removes the associated listener from being notified. Any broadcasts
1457    /// which have already been buffered can still be received, after which
1458    /// [`Listener::recv`] returns `None`.
1459    pub fn clear(&mut self) {
1460        let Some(shared) = self.shared.take() else {
1461            return;
1462        };
1463
1464        let index = mem::take(&mut self.index);
1465
1466        let mut broadcasts = shared.broadcasts.lock().unwrap_or_else(|e| e.into_inner());
1467
1468        let Entry::Occupied(mut e) = broadcasts.entry(self.id) else {
1469            return;
1470        };
1471
1472        _ = e.get_mut().try_remove(index);
1473
1474        if e.get().is_empty() {
1475            e.remove();
1476        }
1477    }
1478}
1479
1480impl<T> Drop for Listener<T> {
1481    #[inline]
1482    fn drop(&mut self) {
1483        self.clear();
1484    }
1485}
1486
1487impl<T> fmt::Debug for Listener<T> {
1488    #[inline]
1489    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1490        f.debug_struct("Listener")
1491            .field("type", &any::type_name::<T>())
1492            .field("id", &self.id)
1493            .finish_non_exhaustive()
1494    }
1495}
1496
1497/// A listener for state changes.
1498///
1499/// Constructed through [`Handle::on_state_change`].
1500pub struct StateListener {
1501    rx: watch::Receiver<State>,
1502    shared: Arc<Shared>,
1503}
1504
1505impl StateListener {
1506    /// Get the most recently observed state.
1507    #[inline]
1508    pub fn state(&self) -> State {
1509        *self.rx.borrow()
1510    }
1511
1512    /// Wait for the state to change and return the new state.
1513    ///
1514    /// Returns `None` if the [`Service`] driving the connection is gone.
1515    #[inline]
1516    pub async fn changed(&mut self) -> Option<State> {
1517        if self.shared.is_gone() {
1518            return None;
1519        }
1520
1521        self.rx.changed().await.ok()?;
1522
1523        if self.shared.is_gone() {
1524            return None;
1525        }
1526
1527        Some(*self.rx.borrow_and_update())
1528    }
1529
1530    /// Wait until the observed state is `state`.
1531    ///
1532    /// Returns `false` if the [`Service`] driving the connection is gone before
1533    /// the state could be observed.
1534    pub async fn wait_until(&mut self, state: State) -> bool {
1535        loop {
1536            if *self.rx.borrow_and_update() == state {
1537                return true;
1538            }
1539
1540            if self.shared.is_gone() {
1541                return false;
1542            }
1543
1544            if self.rx.changed().await.is_err() {
1545                return false;
1546            }
1547        }
1548    }
1549}
1550
1551impl Clone for StateListener {
1552    #[inline]
1553    fn clone(&self) -> Self {
1554        Self {
1555            rx: self.rx.clone(),
1556            shared: self.shared.clone(),
1557        }
1558    }
1559}
1560
1561impl fmt::Debug for StateListener {
1562    #[inline]
1563    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1564        f.debug_struct("StateListener")
1565            .field("state", &*self.rx.borrow())
1566            .finish()
1567    }
1568}
1569
1570/// A raw packet of data.
1571#[derive(Clone)]
1572pub struct RawPacket {
1573    id: MessageId,
1574    buf: Bytes,
1575    at: Cell<usize>,
1576    format: Format,
1577    channel: ChannelId,
1578}
1579
1580impl RawPacket {
1581    /// Construct an empty raw packet.
1582    ///
1583    /// # Examples
1584    ///
1585    /// ```
1586    /// use musli_web::api::MessageId;
1587    /// use musli_web::client::RawPacket;
1588    ///
1589    /// let packet = RawPacket::empty();
1590    ///
1591    /// assert!(packet.is_empty());
1592    /// assert_eq!(packet.id(), MessageId::EMPTY);
1593    /// ```
1594    #[inline]
1595    pub const fn empty() -> Self {
1596        Self {
1597            id: MessageId::EMPTY,
1598            buf: Bytes::new(),
1599            at: Cell::new(0),
1600            format: Format::DEFAULT,
1601            channel: ChannelId::NONE,
1602        }
1603    }
1604
1605    /// The [`Format`] the body of this packet is encoded with.
1606    ///
1607    /// # Examples
1608    ///
1609    /// ```
1610    /// use musli_web::api::Format;
1611    /// use musli_web::client::RawPacket;
1612    ///
1613    /// let packet = RawPacket::empty();
1614    /// assert_eq!(packet.format(), Format::DEFAULT);
1615    /// ```
1616    #[inline]
1617    pub fn format(&self) -> Format {
1618        self.format
1619    }
1620
1621    /// Return the channel this packet belongs to.
1622    ///
1623    /// This is [`ChannelId::NONE`] unless the packet belongs to a response over
1624    /// a channel constructed with [`Handle::channel`].
1625    #[inline]
1626    pub fn channel(&self) -> ChannelId {
1627        self.channel
1628    }
1629
1630    /// Decode the contents of a raw packet.
1631    ///
1632    /// This can be called multiple times if there are multiple payloads in
1633    /// sequence of the response.
1634    ///
1635    /// You can check if the packet is empty using [`RawPacket::is_empty`].
1636    pub fn decode<'this, T>(&'this self) -> Result<T>
1637    where
1638        T: DecodeBody<'this>,
1639    {
1640        if self.id == MessageId::EMPTY {
1641            return Err(Error::new(ErrorKind::EmptyPacket));
1642        }
1643
1644        let mut at = self.at.get();
1645
1646        match self.format.decode(&self.buf, &mut at) {
1647            Ok(value) => {
1648                self.at.set(at);
1649                Ok(value)
1650            }
1651            Err(error) => {
1652                self.at.set(self.len());
1653                Err(Error::decode_packet(error))
1654            }
1655        }
1656    }
1657
1658    /// Get the underlying byte slice of the packet.
1659    ///
1660    /// # Examples
1661    ///
1662    /// ```
1663    /// use musli_web::client::RawPacket;
1664    ///
1665    /// let packet = RawPacket::empty();
1666    /// assert_eq!(packet.as_slice(), &[] as &[u8]);
1667    /// ```
1668    #[inline]
1669    pub fn as_slice(&self) -> &[u8] {
1670        &self.buf
1671    }
1672
1673    /// Get the number of bytes remaining to be decoded in the packet.
1674    ///
1675    /// # Examples
1676    ///
1677    /// ```
1678    /// use musli_web::client::RawPacket;
1679    ///
1680    /// let packet = RawPacket::empty();
1681    /// assert_eq!(packet.remaining(), 0);
1682    /// ```
1683    #[inline]
1684    pub fn remaining(&self) -> usize {
1685        self.buf.len().saturating_sub(self.at.get())
1686    }
1687
1688    /// Get the length of the packet.
1689    ///
1690    /// # Examples
1691    ///
1692    /// ```
1693    /// use musli_web::client::RawPacket;
1694    ///
1695    /// let packet = RawPacket::empty();
1696    /// assert_eq!(packet.len(), 0);
1697    /// ```
1698    #[inline]
1699    pub fn len(&self) -> usize {
1700        self.buf.len()
1701    }
1702
1703    /// Check if the packet is empty.
1704    ///
1705    /// # Examples
1706    ///
1707    /// ```
1708    /// use musli_web::client::RawPacket;
1709    ///
1710    /// let packet = RawPacket::empty();
1711    /// assert!(packet.is_empty());
1712    /// ```
1713    #[inline]
1714    pub fn is_empty(&self) -> bool {
1715        self.at.get() >= self.len()
1716    }
1717
1718    /// The id of the packet this is a response to as specified by
1719    /// [`Endpoint::ID`] or [`Broadcast::ID`].
1720    ///
1721    /// [`Endpoint::ID`]: crate::api::Endpoint::ID
1722    /// [`Broadcast::ID`]: crate::api::Broadcast::ID
1723    #[inline]
1724    pub fn id(&self) -> MessageId {
1725        self.id
1726    }
1727}
1728
1729impl fmt::Debug for RawPacket {
1730    #[inline]
1731    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1732        f.debug_struct("RawPacket")
1733            .field("id", &self.id)
1734            .field("remaining", &self.remaining())
1735            .finish()
1736    }
1737}
1738
1739/// A typed packet of data.
1740pub struct Packet<T> {
1741    raw: RawPacket,
1742    _marker: PhantomData<T>,
1743}
1744
1745impl<T> Packet<T> {
1746    /// Construct an empty packet.
1747    ///
1748    /// # Examples
1749    ///
1750    /// ```
1751    /// use musli_web::api::MessageId;
1752    /// use musli_web::client::Packet;
1753    ///
1754    /// let packet = Packet::<()>::empty();
1755    ///
1756    /// assert!(packet.is_empty());
1757    /// assert_eq!(packet.id(), MessageId::EMPTY);
1758    /// ```
1759    #[inline]
1760    pub const fn empty() -> Self {
1761        Self {
1762            raw: RawPacket::empty(),
1763            _marker: PhantomData,
1764        }
1765    }
1766
1767    /// Construct a new typed packet from a raw one.
1768    ///
1769    /// Note that this does not guarantee that the typed packet is correct, but
1770    /// the `T` parameter becomes associated with it allowing it to be used
1771    /// automatically with methods such as [`Packet::decode`].
1772    #[inline]
1773    pub fn new(raw: RawPacket) -> Self {
1774        Self {
1775            raw,
1776            _marker: PhantomData,
1777        }
1778    }
1779
1780    /// Return the channel this packet belongs to.
1781    ///
1782    /// This is [`ChannelId::NONE`] unless the packet belongs to a response over
1783    /// a channel constructed with [`Handle::channel`].
1784    #[inline]
1785    pub fn channel(&self) -> ChannelId {
1786        self.raw.channel()
1787    }
1788
1789    /// The [`Format`] the body of this packet is encoded with.
1790    #[inline]
1791    pub fn format(&self) -> Format {
1792        self.raw.format()
1793    }
1794
1795    /// Convert a packet into a raw packet.
1796    #[inline]
1797    pub fn into_raw(self) -> RawPacket {
1798        self.raw
1799    }
1800
1801    /// Get the number of bytes remaining to be decoded in the packet.
1802    ///
1803    /// # Examples
1804    ///
1805    /// ```
1806    /// use musli_web::client::Packet;
1807    ///
1808    /// let packet = Packet::<()>::empty();
1809    /// assert_eq!(packet.remaining(), 0);
1810    /// ```
1811    #[inline]
1812    pub fn remaining(&self) -> usize {
1813        self.raw.remaining()
1814    }
1815
1816    /// Check if the packet is empty.
1817    ///
1818    /// # Examples
1819    ///
1820    /// ```
1821    /// use musli_web::client::Packet;
1822    ///
1823    /// let packet = Packet::<()>::empty();
1824    /// assert!(packet.is_empty());
1825    /// ```
1826    #[inline]
1827    pub fn is_empty(&self) -> bool {
1828        self.raw.is_empty()
1829    }
1830
1831    /// The id of the packet this is a response to as specified by
1832    /// [`Endpoint::ID`] or [`Broadcast::ID`].
1833    ///
1834    /// [`Endpoint::ID`]: crate::api::Endpoint::ID
1835    /// [`Broadcast::ID`]: crate::api::Broadcast::ID
1836    #[inline]
1837    pub fn id(&self) -> MessageId {
1838        self.raw.id()
1839    }
1840}
1841
1842impl<T> Packet<T>
1843where
1844    T: api::Decodable,
1845{
1846    /// Decode the contents of a packet.
1847    ///
1848    /// This can be called multiple times if there are multiple payloads in
1849    /// sequence of the response.
1850    ///
1851    /// You can check if the packet is empty using [`Packet::is_empty`].
1852    #[inline]
1853    pub fn decode(&self) -> Result<T::Type<'_>> {
1854        self.decode_any()
1855    }
1856
1857    /// Decode any contents of a packet.
1858    ///
1859    /// This can be called multiple times if there are multiple payloads in
1860    /// sequence of the response.
1861    ///
1862    /// You can check if the packet is empty using [`Packet::is_empty`].
1863    #[inline]
1864    pub fn decode_any<'de, R>(&'de self) -> Result<R>
1865    where
1866        R: DecodeBody<'de>,
1867    {
1868        self.raw.decode()
1869    }
1870}
1871
1872impl<T> Packet<T>
1873where
1874    T: api::Endpoint,
1875{
1876    /// Decode the response of a packet.
1877    ///
1878    /// This can be called multiple times if there are multiple payloads in
1879    /// sequence of the response.
1880    ///
1881    /// You can check if the packet is empty using [`Packet::is_empty`].
1882    #[inline]
1883    pub fn decode_response(&self) -> Result<T::Response<'_>> {
1884        self.decode_any_response()
1885    }
1886
1887    /// Decode any response of a packet.
1888    ///
1889    /// This can be called multiple times if there are multiple payloads in
1890    /// sequence of the response.
1891    ///
1892    /// You can check if the packet is empty using [`Packet::is_empty`].
1893    #[inline]
1894    pub fn decode_any_response<'de, R>(&'de self) -> Result<R>
1895    where
1896        R: DecodeBody<'de>,
1897    {
1898        self.raw.decode()
1899    }
1900}
1901
1902impl<T> Packet<T>
1903where
1904    T: api::Broadcast,
1905{
1906    /// Decode the primary event related to a broadcast.
1907    #[inline]
1908    pub fn decode_event<'de>(&'de self) -> Result<T::Event<'de>>
1909    where
1910        T: api::BroadcastWithEvent,
1911    {
1912        self.decode_event_any()
1913    }
1914
1915    /// Decode any event related to a broadcast.
1916    #[inline]
1917    pub fn decode_event_any<'de, E>(&'de self) -> Result<E>
1918    where
1919        E: Event<Broadcast = T> + DecodeBody<'de>,
1920    {
1921        self.raw.decode()
1922    }
1923}
1924
1925impl<T> Clone for Packet<T> {
1926    #[inline]
1927    fn clone(&self) -> Self {
1928        Self {
1929            raw: self.raw.clone(),
1930            _marker: PhantomData,
1931        }
1932    }
1933}
1934
1935impl<T> fmt::Debug for Packet<T> {
1936    #[inline]
1937    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1938        f.debug_struct("Packet")
1939            .field("type", &any::type_name::<T>())
1940            .field("remaining", &self.remaining())
1941            .finish()
1942    }
1943}