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}