Skip to main content

musli_web/
api.rs

1//! Shared traits for defining API types.
2//!
3//! # Wire format
4//!
5//! Every websocket message is a binary frame consisting of a fixed *envelope*
6//! followed by an optional *body*:
7//!
8//! ```text
9//! +--------------------------+--------------------------------+
10//! | envelope (musli::packed) | body (negotiated api::Format)  |
11//! +--------------------------+--------------------------------+
12//! ```
13//!
14//! The envelope is a [`RequestHeader`] for messages sent by the client and a
15//! [`ResponseHeader`] for messages sent by the server. It is *always* encoded
16//! with [`musli::packed`], is a fixed size, and never changes with the
17//! negotiated format. This is what makes the format negotiable at all, since
18//! both peers can always read the envelope regardless of what they have agreed
19//! on for bodies.
20//!
21//! The body is encoded with the [`Format`] identified by the `format` field of
22//! the envelope, so every message is self-describing in this respect. A `format`
23//! of zero means the message carries no body.
24//!
25//! # Negotiating the format
26//!
27//! A client picks the [`Format`] it wants to use, defaulting to
28//! [`Format::DEFAULT`]. The exchange is:
29//!
30//! 1. The server sends [`MessageId::SERVER_HELLO`] as soon as the connection is
31//!    established. This carries no body.
32//! 2. The client responds with a [`MessageId::NEGOTIATE`] request whose
33//!    envelope carries the desired [`Format`]. This carries no body.
34//! 3. If the server supports that format it records it for the connection and
35//!    replies with an empty response whose envelope carries the format that was
36//!    accepted. If it does not, it replies with an error listing the formats it
37//!    does support, and the connection settles on [`Format::DEFAULT`].
38//!
39//! Both peers are forced through this before anything else can happen:
40//!
41//! * A client only reports itself as connected once step 3 has resolved.
42//! * A server has no way to write a message until then either, since
43//!   [`ws::Connect::connect`] is what produces the [`ws::Server`] which can, and
44//!   it does not resolve until step 3 has been flushed. Any other message
45//!   arriving in place of step 2 is a protocol violation which closes the
46//!   connection.
47//!
48//! Together this guarantees that server-initiated messages such as broadcasts
49//! are encoded with a format the client understands. The format is fixed for
50//! the lifetime of a connection, so a second negotiation is refused — a client
51//! which wants a different one reconnects.
52//!
53//! Requests additionally carry their own format in the envelope, so the server
54//! decodes each request body with the format that request declares and replies
55//! in the same format. Negotiation therefore only matters for messages the
56//! server sends on its own initiative.
57//!
58//! Since formats are gated behind [features], a server may genuinely be unable
59//! to speak a format a client asks for, which is why step 3 can fail.
60//!
61//! [features]: <https://docs.rs/musli-web/latest/musli_web/#features>
62//! [`ws::Connect::connect`]: <https://docs.rs/musli-web/latest/musli_web/ws/struct.Connect.html#method.connect>
63//! [`ws::Server`]: <https://docs.rs/musli-web/latest/musli_web/ws/struct.Server.html>
64
65use core::fmt;
66use core::num::NonZeroU16;
67use core::sync::atomic::{AtomicU16, Ordering};
68
69use musli::alloc::Global;
70use musli::mode::{Binary, Text};
71use musli::{Decode, Encode};
72
73#[doc(inline)]
74pub use musli_web_macros::define;
75
76/// The serialization format used for message bodies.
77///
78/// The format is negotiated per connection, see the [negotiation protocol] for the
79/// details of how. Message *headers* are never affected by this and always use
80/// a fixed envelope, which is what makes negotiation possible in the first
81/// place.
82///
83/// The variants are ordered from least to most capable. Each capability that is
84/// dropped makes the encoding more compact:
85///
86/// | | `reorder` | `missing` | `unknown` | `self` |
87/// |-|-|-|-|-|
88/// | [`Packed`] | ✗ | ✗ | ✗ | ✗ |
89/// | [`Storage`] | ✔ | ✔ | ✗ | ✗ |
90/// | [`Wire`] | ✔ | ✔ | ✔ | ✗ |
91/// | [`Descriptive`] | ✔ | ✔ | ✔ | ✔ |
92/// | [`Json`] | ✔ | ✔ | ✔ | ✔ |
93///
94/// * `reorder` determines whether fields may be reordered in the model.
95/// * `missing` determines whether decoding tolerates missing fields, which is
96///   what allows new optional fields to be added.
97/// * `unknown` determines whether decoding can skip fields it does not know
98///   about. A format which can do this is *fully upgrade safe*, since an old
99///   peer can talk to a new one.
100/// * `self` determines whether the format is self-descriptive, so that the data
101///   can be decoded without the model.
102///
103/// [`Packed`]: Format::Packed
104/// [`Storage`]: Format::Storage
105/// [`Wire`]: Format::Wire
106/// [`Descriptive`]: Format::Descriptive
107/// [`Json`]: Format::Json
108///
109/// # Examples
110///
111/// ```
112/// use musli_web::api::Format;
113///
114/// assert_eq!(Format::default(), Format::Wire);
115/// assert!(Format::Wire.is_upgrade_safe());
116/// assert!(!Format::Storage.is_upgrade_safe());
117/// assert!(Format::Json.is_human_readable());
118/// ```
119#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
120#[non_exhaustive]
121pub enum Format {
122    /// The [`musli::packed`] format.
123    ///
124    /// The most compact format, but it requires that both peers use exactly the
125    /// same model. Suitable when client and server are deployed together.
126    Packed,
127    /// The [`musli::storage`] format.
128    ///
129    /// Tolerates missing fields, but cannot skip fields it does not know about.
130    Storage,
131    /// The [`musli::wire`] format.
132    ///
133    /// Fully upgrade safe, so peers built against different versions of the
134    /// model can talk to each other. This is the default.
135    Wire,
136    /// The [`musli::descriptive`] format.
137    ///
138    /// Fully upgrade safe and self-descriptive, at the cost of a larger
139    /// payload.
140    Descriptive,
141    /// The [`musli::json`] format.
142    ///
143    /// Human readable, which is useful when the traffic has to be inspected by
144    /// hand. Encoded using the [`Text`] mode so that fields are keyed by name.
145    ///
146    /// [`Text`]: musli::mode::Text
147    Json,
148}
149
150impl Format {
151    /// The default format, which is [`Format::Wire`].
152    ///
153    /// This is used by a client which has not picked a format, and by a server
154    /// for a connection which has not negotiated one.
155    ///
156    /// # Examples
157    ///
158    /// ```
159    /// use musli_web::api::Format;
160    ///
161    /// assert_eq!(Format::DEFAULT, Format::Wire);
162    /// ```
163    pub const DEFAULT: Self = Self::Wire;
164
165    /// Every format in order of increasing capability.
166    ///
167    /// Note that this includes formats which the crate might not have been
168    /// built with support for, see [`Format::is_supported`].
169    ///
170    /// # Examples
171    ///
172    /// ```
173    /// use musli_web::api::Format;
174    ///
175    /// assert!(Format::ALL.contains(&Format::Json));
176    /// ```
177    pub const ALL: &'static [Format] = &[
178        Format::Packed,
179        Format::Storage,
180        Format::Wire,
181        Format::Descriptive,
182        Format::Json,
183    ];
184
185    /// Get the stable identifier used for this format on the wire.
186    ///
187    /// Zero is never used, so it is available to indicate an absent format.
188    ///
189    /// # Examples
190    ///
191    /// ```
192    /// use musli_web::api::Format;
193    ///
194    /// assert_eq!(Format::Wire.to_u8(), 3);
195    /// assert_eq!(Format::from_u8(3), Some(Format::Wire));
196    /// ```
197    #[inline]
198    pub const fn to_u8(self) -> u8 {
199        match self {
200            Format::Packed => 1,
201            Format::Storage => 2,
202            Format::Wire => 3,
203            Format::Descriptive => 4,
204            Format::Json => 5,
205        }
206    }
207
208    /// Construct a format from the stable identifier used on the wire.
209    ///
210    /// Returns `None` if the identifier is not known, which is how a peer
211    /// built against an older version of this crate reports a format it has
212    /// never heard of.
213    ///
214    /// # Examples
215    ///
216    /// ```
217    /// use musli_web::api::Format;
218    ///
219    /// assert_eq!(Format::from_u8(1), Some(Format::Packed));
220    /// assert_eq!(Format::from_u8(0), None);
221    /// assert_eq!(Format::from_u8(200), None);
222    /// ```
223    #[inline]
224    pub const fn from_u8(id: u8) -> Option<Self> {
225        match id {
226            1 => Some(Format::Packed),
227            2 => Some(Format::Storage),
228            3 => Some(Format::Wire),
229            4 => Some(Format::Descriptive),
230            5 => Some(Format::Json),
231            _ => None,
232        }
233    }
234
235    /// The name of the format.
236    ///
237    /// # Examples
238    ///
239    /// ```
240    /// use musli_web::api::Format;
241    ///
242    /// assert_eq!(Format::Wire.name(), "wire");
243    /// ```
244    #[inline]
245    pub const fn name(self) -> &'static str {
246        match self {
247            Format::Packed => "packed",
248            Format::Storage => "storage",
249            Format::Wire => "wire",
250            Format::Descriptive => "descriptive",
251            Format::Json => "json",
252        }
253    }
254
255    /// Test if the format can skip over unknown fields, making it fully upgrade
256    /// safe.
257    ///
258    /// # Examples
259    ///
260    /// ```
261    /// use musli_web::api::Format;
262    ///
263    /// assert!(Format::Wire.is_upgrade_safe());
264    /// assert!(!Format::Packed.is_upgrade_safe());
265    /// ```
266    #[inline]
267    pub const fn is_upgrade_safe(self) -> bool {
268        matches!(self, Format::Wire | Format::Descriptive | Format::Json)
269    }
270
271    /// Test if the format is self-descriptive, so that data can be decoded
272    /// without access to the model.
273    ///
274    /// # Examples
275    ///
276    /// ```
277    /// use musli_web::api::Format;
278    ///
279    /// assert!(Format::Descriptive.is_self_descriptive());
280    /// assert!(!Format::Wire.is_self_descriptive());
281    /// ```
282    #[inline]
283    pub const fn is_self_descriptive(self) -> bool {
284        matches!(self, Format::Descriptive | Format::Json)
285    }
286
287    /// Test if the format produces output which is meant to be read by humans.
288    ///
289    /// # Examples
290    ///
291    /// ```
292    /// use musli_web::api::Format;
293    ///
294    /// assert!(Format::Json.is_human_readable());
295    /// assert!(!Format::Wire.is_human_readable());
296    /// ```
297    #[inline]
298    pub const fn is_human_readable(self) -> bool {
299        matches!(self, Format::Json)
300    }
301}
302
303impl Default for Format {
304    /// Construct the default format, which is [`Format::DEFAULT`].
305    ///
306    /// # Examples
307    ///
308    /// ```
309    /// use musli_web::api::Format;
310    ///
311    /// assert_eq!(Format::default(), Format::Wire);
312    /// ```
313    #[inline]
314    fn default() -> Self {
315        Self::DEFAULT
316    }
317}
318
319impl fmt::Display for Format {
320    #[inline]
321    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
322        f.write_str(self.name())
323    }
324}
325
326/// Types which can be encoded in every mode that this crate supports.
327///
328/// This is a blanket trait covering [`Encode`] in both the [`Binary`] and
329/// [`Text`] modes, which is what allows a message body to be encoded with any
330/// [`Format`] including [`Format::Json`].
331///
332/// Deriving [`Encode`] implements every mode, so this is implemented
333/// automatically unless the type has been restricted to a specific mode.
334pub trait EncodeBody
335where
336    Self: Encode<Binary> + Encode<Text>,
337{
338}
339
340impl<T> EncodeBody for T where T: ?Sized + Encode<Binary> + Encode<Text> {}
341
342/// Types which can be decoded in every mode that this crate supports.
343///
344/// This is a blanket trait covering [`Decode`] in both the [`Binary`] and
345/// [`Text`] modes, which is what allows a message body to be decoded with any
346/// [`Format`] including [`Format::Json`].
347///
348/// Deriving [`Decode`] implements every mode, so this is implemented
349/// automatically unless the type has been restricted to a specific mode.
350pub trait DecodeBody<'de>
351where
352    Self: Decode<'de, Binary, Global> + Decode<'de, Text, Global>,
353{
354}
355
356impl<'de, T> DecodeBody<'de> for T where T: Decode<'de, Binary, Global> + Decode<'de, Text, Global> {}
357
358/// A trait for constructing identifiers.
359pub trait Id
360where
361    Self: 'static + Send + Sized + fmt::Debug,
362{
363    /// Get the raw message identifier for this type.
364    fn id(&self) -> MessageId;
365
366    /// Construct an identifier from a raw message identifier.
367    fn from_id(id: MessageId) -> Self;
368
369    #[doc(hidden)]
370    fn __do_not_implement_id();
371}
372
373/// A unique and opaque identifier for a channel over the websocket.
374#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Encode, Decode)]
375#[musli(transparent)]
376pub struct ChannelId {
377    repr: u16,
378}
379
380impl ChannelId {
381    /// The channel id used for an invalid channel.
382    pub const NONE: Self = Self::from_u16(0);
383
384    /// Construct a new channel id from a raw `u16` representation.
385    ///
386    /// Note that this does not guarantee that the internal representation of a
387    /// channel identifier is exactly a `u16`, only that at least `u16` unique
388    /// identifiers can be constructed.
389    ///
390    /// Using `0` is equivalent to [`ChannelId::NONE`]. When implementing a
391    /// custom [`ChannelAllocator`] the allocator must avoid constructor
392    /// identifiers with this value since it is equivalent to no channel.
393    ///
394    /// [`ChannelAllocator`]: crate::ws::ChannelAllocator
395    ///
396    /// # Examples
397    ///
398    /// ```
399    /// use musli_web::api::ChannelId;
400    /// let id = ChannelId::from_u16(0);
401    /// assert_eq!(id, ChannelId::NONE);
402    /// ```
403    #[inline]
404    pub const fn from_u16(repr: u16) -> Self {
405        Self { repr }
406    }
407
408    /// Get the raw channel identifier.
409    #[inline]
410    #[cfg(feature = "ws")]
411    pub(crate) const fn raw(&self) -> u16 {
412        self.repr
413    }
414}
415
416impl fmt::Debug for ChannelId {
417    #[inline]
418    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
419        if self.repr == 0 {
420            f.write_str("NONE")
421        } else {
422            write!(f, "{:04x}", self.repr)
423        }
424    }
425}
426
427/// A [`ChannelId`] which can be shared and updated atomically.
428///
429/// This behaves like an atomic variable holding a [`ChannelId`], where the
430/// value can be read, set, replaced, or taken. Each operation takes an
431/// [`Ordering`] which is passed through to the underlying atomic.
432///
433/// [`Ordering`]: core::sync::atomic::Ordering
434///
435/// # Examples
436///
437/// ```
438/// use core::sync::atomic::Ordering;
439///
440/// use musli_web::api::{AtomicChannelId, ChannelId};
441///
442/// let channel = AtomicChannelId::NONE;
443/// assert_eq!(channel.load(Ordering::Acquire), ChannelId::NONE);
444///
445/// channel.store(ChannelId::from_u16(42), Ordering::Release);
446/// assert_eq!(channel.load(Ordering::Acquire), ChannelId::from_u16(42));
447///
448/// assert_eq!(channel.take(Ordering::AcqRel), ChannelId::from_u16(42));
449/// assert_eq!(channel.load(Ordering::Acquire), ChannelId::NONE);
450/// ```
451#[repr(transparent)]
452pub struct AtomicChannelId {
453    repr: AtomicU16,
454}
455
456impl AtomicChannelId {
457    /// An atomic channel id which contains [`ChannelId::NONE`].
458    ///
459    /// Since this is a constant every use of it constructs a new value, to
460    /// share one it has to be bound to a `static` or a variable.
461    ///
462    /// # Examples
463    ///
464    /// ```
465    /// use core::sync::atomic::Ordering;
466    ///
467    /// use musli_web::api::{AtomicChannelId, ChannelId};
468    ///
469    /// let channel = AtomicChannelId::NONE;
470    /// assert_eq!(channel.load(Ordering::Acquire), ChannelId::NONE);
471    /// ```
472    #[allow(clippy::declare_interior_mutable_const)]
473    pub const NONE: Self = Self::new(ChannelId::NONE);
474
475    /// Construct a new atomic channel id containing `id`.
476    ///
477    /// # Examples
478    ///
479    /// ```
480    /// use core::sync::atomic::Ordering;
481    ///
482    /// use musli_web::api::{AtomicChannelId, ChannelId};
483    ///
484    /// let channel = AtomicChannelId::new(ChannelId::from_u16(1));
485    /// assert_eq!(channel.load(Ordering::Acquire), ChannelId::from_u16(1));
486    /// ```
487    #[inline]
488    pub const fn new(id: ChannelId) -> Self {
489        Self {
490            repr: AtomicU16::new(id.repr),
491        }
492    }
493
494    /// Read the current channel id.
495    ///
496    /// `ordering` describes the memory ordering of this operation. Possible
497    /// values are [`SeqCst`], [`Acquire`] and [`Relaxed`].
498    ///
499    /// [`SeqCst`]: Ordering::SeqCst
500    /// [`Acquire`]: Ordering::Acquire
501    /// [`Relaxed`]: Ordering::Relaxed
502    ///
503    /// # Panics
504    ///
505    /// Panics if `ordering` is [`Release`] or [`AcqRel`].
506    ///
507    /// [`Release`]: Ordering::Release
508    /// [`AcqRel`]: Ordering::AcqRel
509    ///
510    /// # Examples
511    ///
512    /// ```
513    /// use core::sync::atomic::Ordering;
514    ///
515    /// use musli_web::api::{AtomicChannelId, ChannelId};
516    ///
517    /// let channel = AtomicChannelId::new(ChannelId::from_u16(1));
518    /// assert_eq!(channel.load(Ordering::Acquire), ChannelId::from_u16(1));
519    /// ```
520    #[inline]
521    pub fn load(&self, ordering: Ordering) -> ChannelId {
522        ChannelId::from_u16(self.repr.load(ordering))
523    }
524
525    /// Set the current channel id to `id`, discarding the old value.
526    ///
527    /// `ordering` describes the memory ordering of this operation. Possible
528    /// values are [`SeqCst`], [`Release`] and [`Relaxed`].
529    ///
530    /// [`SeqCst`]: Ordering::SeqCst
531    /// [`Release`]: Ordering::Release
532    /// [`Relaxed`]: Ordering::Relaxed
533    ///
534    /// # Panics
535    ///
536    /// Panics if `ordering` is [`Acquire`] or [`AcqRel`].
537    ///
538    /// [`Acquire`]: Ordering::Acquire
539    /// [`AcqRel`]: Ordering::AcqRel
540    ///
541    /// # Examples
542    ///
543    /// ```
544    /// use core::sync::atomic::Ordering;
545    ///
546    /// use musli_web::api::{AtomicChannelId, ChannelId};
547    ///
548    /// let channel = AtomicChannelId::NONE;
549    /// channel.store(ChannelId::from_u16(1), Ordering::Release);
550    /// assert_eq!(channel.load(Ordering::Acquire), ChannelId::from_u16(1));
551    /// ```
552    #[inline]
553    pub fn store(&self, id: ChannelId, ordering: Ordering) {
554        self.repr.store(id.repr, ordering);
555    }
556
557    /// Replace the current channel id with `id`, returning the old value.
558    ///
559    /// `ordering` describes the memory ordering of this operation. All
560    /// orderings are possible. Note that using [`Acquire`] makes the store part
561    /// of this operation [`Relaxed`], and using [`Release`] makes the load part
562    /// [`Relaxed`].
563    ///
564    /// [`Acquire`]: Ordering::Acquire
565    /// [`Release`]: Ordering::Release
566    /// [`Relaxed`]: Ordering::Relaxed
567    ///
568    /// # Examples
569    ///
570    /// ```
571    /// use core::sync::atomic::Ordering;
572    ///
573    /// use musli_web::api::{AtomicChannelId, ChannelId};
574    ///
575    /// let channel = AtomicChannelId::new(ChannelId::from_u16(1));
576    ///
577    /// let old = channel.replace(ChannelId::from_u16(2), Ordering::AcqRel);
578    /// assert_eq!(old, ChannelId::from_u16(1));
579    /// assert_eq!(channel.load(Ordering::Acquire), ChannelId::from_u16(2));
580    /// ```
581    #[inline]
582    pub fn replace(&self, id: ChannelId, ordering: Ordering) -> ChannelId {
583        ChannelId::from_u16(self.repr.swap(id.repr, ordering))
584    }
585
586    /// Take the current channel id, leaving [`ChannelId::NONE`] in its place.
587    ///
588    /// `ordering` describes the memory ordering of this operation. All
589    /// orderings are possible, see [`replace`] for details.
590    ///
591    /// [`replace`]: AtomicChannelId::replace
592    ///
593    /// # Examples
594    ///
595    /// ```
596    /// use core::sync::atomic::Ordering;
597    ///
598    /// use musli_web::api::{AtomicChannelId, ChannelId};
599    ///
600    /// let channel = AtomicChannelId::new(ChannelId::from_u16(1));
601    ///
602    /// assert_eq!(channel.take(Ordering::AcqRel), ChannelId::from_u16(1));
603    /// assert_eq!(channel.load(Ordering::Acquire), ChannelId::NONE);
604    /// assert_eq!(channel.take(Ordering::AcqRel), ChannelId::NONE);
605    /// ```
606    #[inline]
607    pub fn take(&self, ordering: Ordering) -> ChannelId {
608        self.replace(ChannelId::NONE, ordering)
609    }
610
611    /// Consume the atomic channel id, returning the contained value.
612    ///
613    /// Since this takes ownership no synchronization is needed.
614    ///
615    /// # Examples
616    ///
617    /// ```
618    /// use musli_web::api::{AtomicChannelId, ChannelId};
619    ///
620    /// let channel = AtomicChannelId::new(ChannelId::from_u16(1));
621    /// assert_eq!(channel.into_inner(), ChannelId::from_u16(1));
622    /// ```
623    #[inline]
624    pub fn into_inner(self) -> ChannelId {
625        ChannelId::from_u16(self.repr.into_inner())
626    }
627}
628
629impl Default for AtomicChannelId {
630    /// Construct an atomic channel id containing [`ChannelId::NONE`].
631    ///
632    /// # Examples
633    ///
634    /// ```
635    /// use core::sync::atomic::Ordering;
636    ///
637    /// use musli_web::api::{AtomicChannelId, ChannelId};
638    ///
639    /// let channel = AtomicChannelId::default();
640    /// assert_eq!(channel.load(Ordering::Acquire), ChannelId::NONE);
641    /// ```
642    #[inline]
643    fn default() -> Self {
644        Self::NONE
645    }
646}
647
648impl From<ChannelId> for AtomicChannelId {
649    #[inline]
650    fn from(id: ChannelId) -> Self {
651        Self::new(id)
652    }
653}
654
655impl fmt::Debug for AtomicChannelId {
656    #[inline]
657    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
658        self.load(Ordering::Relaxed).fmt(f)
659    }
660}
661
662/// A raw identifier for a message.
663#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Encode, Decode)]
664#[repr(transparent)]
665#[musli(transparent)]
666pub struct MessageId(NonZeroU16);
667
668impl fmt::Display for MessageId {
669    #[inline]
670    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
671        self.0.fmt(f)
672    }
673}
674
675impl MessageId {
676    /// The message id for [`ErrorMessage`].
677    pub const ERROR_MESSAGE: Self = unsafe { Self::new_unchecked((i16::MAX as u16) + 1) };
678
679    /// A connect of a channel.
680    pub const CONNECT: Self = unsafe { Self::new_unchecked((i16::MAX as u16) + 2) };
681
682    /// A clean disconnect of a channel.
683    pub const DISCONNECT: Self = unsafe { Self::new_unchecked((i16::MAX as u16) + 3) };
684
685    /// The first message the server sends to indicat that a connection is open.
686    pub const SERVER_HELLO: Self = unsafe { Self::new_unchecked((i16::MAX as u16) + 4) };
687
688    /// A request from the client to use a particular [`Format`] for the
689    /// remainder of the connection.
690    ///
691    /// See the [negotiation protocol] for how this is used.
692    pub const NEGOTIATE: Self = unsafe { Self::new_unchecked((i16::MAX as u16) + 5) };
693
694    /// The message id for an empty packet constructed using [`Packet::empty`]
695    /// or [`RawPacket::empty`].
696    ///
697    /// [`Packet::empty`]: crate::web::Packet::empty
698    /// [`RawPacket::empty`]: crate::web::RawPacket::empty
699    ///
700    /// # Examples
701    ///
702    /// ```
703    /// use musli_web::api::MessageId;
704    /// use musli_web::web::{RawPacket, Packet};
705    ///
706    /// let packet = RawPacket::empty();
707    /// assert_eq!(packet.id(), MessageId::EMPTY);
708    ///
709    /// let packet = Packet::<()>::empty();
710    /// assert_eq!(packet.id(), MessageId::EMPTY);
711    /// ```
712    pub const EMPTY: Self = unsafe { Self::new_unchecked(u16::MAX) };
713
714    /// Construct a raw message id.
715    #[inline]
716    pub const fn new(id: u16) -> Option<Self> {
717        let Some(value) = NonZeroU16::new(id) else {
718            return None;
719        };
720
721        Some(Self(value))
722    }
723
724    /// Get a raw message identifier.
725    #[inline]
726    pub const fn get(&self) -> u16 {
727        self.0.get()
728    }
729
730    /// Construct a new message ID.
731    ///
732    /// # Safety
733    ///
734    /// The caller must ensure that the provided `id` is non-zero.
735    #[inline]
736    pub const unsafe fn new_unchecked(id: u16) -> Self {
737        Self(unsafe { NonZeroU16::new_unchecked(id) })
738    }
739}
740
741/// A trait implemented for types which can be decoded into something.
742///
743/// Do not implement manually, instead use the [`define!`] macro.
744pub trait Decodable {
745    /// The decodable type related to this.
746    type Type<'de>: DecodeBody<'de>;
747
748    #[doc(hidden)]
749    fn __do_not_implement_decodable();
750}
751
752/// An endpoint marker trait.
753///
754/// Do not implement manually, instead use the [`define!`] macro.
755pub trait Endpoint
756where
757    Self: 'static,
758    for<'de> Self: Decodable<Type<'de> = Self::Response<'de>>,
759{
760    /// The kind of the endpoint.
761    const ID: MessageId;
762
763    /// The primary response type related to the endpoint.
764    type Response<'de>: DecodeBody<'de>;
765
766    #[doc(hidden)]
767    fn __do_not_implement_endpoint();
768}
769
770/// The marker trait used for broadcasts.
771///
772/// Do not implement manually, instead use the [`define!`] macro.
773pub trait Broadcast
774where
775    Self: 'static,
776{
777    /// The kind of the broadcast.
778    const ID: MessageId;
779
780    #[doc(hidden)]
781    fn __do_not_implement_broadcast();
782}
783
784/// Trait implemented for broadcasts which have a primary event.
785pub trait BroadcastWithEvent
786where
787    Self: Broadcast,
788    for<'de> Self: Decodable<Type<'de> = Self::Event<'de>>,
789{
790    /// The event type related to the broadcast.
791    type Event<'de>: Event<Broadcast = Self> + DecodeBody<'de>
792    where
793        Self: 'de;
794
795    #[doc(hidden)]
796    fn __do_not_implement_broadcast_with_event();
797}
798
799/// A marker indicating a request type.
800///
801/// Do not implement manually, instead use the [`define!`] macro.
802pub trait Request
803where
804    Self: EncodeBody,
805{
806    /// The endpoint related to the request.
807    type Endpoint: Endpoint;
808
809    #[doc(hidden)]
810    fn __do_not_implement_request();
811}
812
813/// The event of a broadcast.
814///
815/// Do not implement manually, instead use the [`define!`] macro.
816pub trait Event
817where
818    Self: EncodeBody,
819{
820    /// The endpoint related to the broadcast.
821    type Broadcast: Broadcast;
822
823    #[doc(hidden)]
824    fn __do_not_implement_event();
825}
826
827/// A request to connect.
828#[derive(Debug, Clone, Copy, Encode, Decode)]
829#[doc(hidden)]
830#[musli(packed)]
831pub struct Connect;
832
833/// The header of a response.
834///
835/// This is part of the fixed envelope, see the [negotiation protocol].
836#[derive(Debug, Clone, Encode, Decode)]
837#[doc(hidden)]
838#[musli(packed)]
839pub struct ResponseHeader {
840    /// The serial request this is a response to.
841    pub serial: u32,
842    /// This is a broadcast over the specified type. If this is non-empty the
843    /// serial is 0.
844    pub broadcast: u16,
845    /// If non-zero, the response contains an error of the given type.
846    pub error: u16,
847    /// The [`Format`] the body of this response is encoded with, as given by
848    /// [`Format::to_u8`]. Zero if the response carries no body.
849    pub format: u8,
850    /// The channel over which the response will be sent.
851    pub channel: ChannelId,
852}
853
854/// An error response.
855#[derive(Debug, Clone, Encode, Decode)]
856#[doc(hidden)]
857#[musli(packed)]
858pub struct ErrorMessage<'de> {
859    /// The error message.
860    pub message: &'de str,
861}
862
863/// A request header.
864///
865/// This is part of the fixed envelope, see the [negotiation protocol].
866#[derive(Debug, Clone, Copy, Encode, Decode)]
867#[doc(hidden)]
868#[musli(packed)]
869pub struct RequestHeader {
870    /// The serial of the request.
871    pub serial: u32,
872    /// The kind of the request.
873    pub id: u16,
874    /// The [`Format`] the body of this request is encoded with, as given by
875    /// [`Format::to_u8`]. Zero if the request carries no body.
876    pub format: u8,
877    /// The channel over which the request was received.
878    pub channel: ChannelId,
879}