musli_web/api.rs
1//! Shared traits for defining API types.
2//!
3//! # Wire format
4//!
5//! Every websocket message consists of a fixed *envelope* followed by an
6//! optional *body*:
7//!
8//! ```text
9//! +--------------------------+--------------------------------+
10//! | envelope (api::Mode) | body (negotiated api::Format) |
11//! +--------------------------+--------------------------------+
12//! ```
13//!
14//! The envelope is a block of *headers*: a [`RequestHeader`] for messages sent
15//! by the client and a [`ResponseHeader`] for messages sent by the server. How
16//! it is spelled is decided by the [`Mode`] the socket runs in and never
17//! changes with the negotiated format. This is what makes the format negotiable
18//! at all, since both peers can always read the envelope regardless of what
19//! they have agreed on for bodies.
20//!
21//! The body is encoded with the [`Format`] identified by the `format` header 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//! # The protocol version
26//!
27//! Every envelope states the version of the wire protocol it was written
28//! against in its `version` header, and a peer refuses an envelope which states
29//! anything other than [`VERSION`]. The very first message each side sees
30//! carries it — [`MessageId::SERVER_HELLO`] for the client and
31//! [`MessageId::NEGOTIATE`] for the server — so a session between peers which
32//! do not speak the same protocol is rejected before either of them has acted
33//! on anything the other said.
34//!
35//! This is what a header being unknown is measured against. A version bump is
36//! the deliberate way to change what the envelope may contain, so an unknown
37//! header arriving under a version we do accept means something has gone wrong
38//! that neither peer planned for, which is why it is refused outright rather
39//! than skipped.
40//!
41//! # Headers
42//!
43//! Every header carries an unsigned integer and is addressed by a name. A
44//! header which is absent reads as zero, and headers may arrive in any order,
45//! so a message which has no use for a header simply does not write one.
46//!
47//! Both spellings are *self-delimiting*: a reader can find the end of a header
48//! it has never seen without understanding what it means. That is deliberate,
49//! and it is what makes the next rule possible.
50//!
51//! **A header which this build does not know about is refused.** It is read,
52//! stepped over so that the position of everything after it is still known, and
53//! then reported as an error which tears the connection down. A peer writing a
54//! header we have never seen is a peer speaking a protocol we do not have, and
55//! acting on the part of the message we happen to recognize would mean acting
56//! on a message we have not actually understood.
57//!
58//! A version which does not match is reported ahead of an unknown header, since
59//! the version is what explains the header.
60//!
61//! # The binary envelope
62//!
63//! In [`Mode::Binary`] each header is a tag byte followed by its value, and a
64//! tag byte of zero ends the block:
65//!
66//! ```text
67//! +-----+---------------+ +-----+
68//! | tag | value (1/2/4) | ... | 0 |
69//! +-----+---------------+ +-----+
70//! ```
71//!
72//! The low six bits of the tag are the header's id, which is never zero. The
73//! top two bits say how wide the value is — `0` for one byte, `1` for two and
74//! `2` for four, little endian — which is what lets an unknown header be
75//! stepped over. A value is written in the narrowest of those which holds it,
76//! so a small value costs no more than it has to. The remaining class is
77//! reserved, and a header which uses it cannot be skipped, so it is refused.
78//!
79//! # The text envelope
80//!
81//! In [`Mode::Text`] the envelope is instead a block of `<key>: <value>` lines
82//! terminated by an empty line, carried in a text frame:
83//!
84//! ```text
85//! version: 2
86//! serial: 1
87//! id: 1
88//! format: 5
89//! channel: 0
90//! app: 0
91//!
92//! {"message":"Hello!"}
93//! ```
94//!
95//! Every header is written, in the order it is declared, with its value in
96//! decimal. Being explicit is worth more than being terse in a spelling meant
97//! to be read by people, so a header which is zero is written out too.
98//!
99//! Nothing about the message is otherwise different, which means the mode only
100//! decides how the frame is *spelled*. Since a text frame has to be valid UTF-8
101//! in its entirety the body has to be human readable too, so [`Mode::Text`]
102//! requires a [`Format`] for which [`Format::is_human_readable`] holds. This is
103//! what [`Mode::accepts`] tests.
104//!
105//! # Negotiating the mode
106//!
107//! Unlike the format, the mode is never carried in a field. The frame type
108//! *is* the mode, so a peer can always tell which envelope it is looking at,
109//! and a message can be read before anything has been agreed on.
110//!
111//! A client sends its [`MessageId::NEGOTIATE`] request in the mode it wants,
112//! and the server replies in the mode it settled on. A server which is asked
113//! for [`Mode::Text`] together with a format it cannot carry that way settles
114//! on [`Mode::DEFAULT`], exactly like it does for a format it does not support.
115//! The client adopts whichever mode the reply arrived in, and the mode is then
116//! fixed for the lifetime of the connection.
117//!
118//! The one message which precedes all of this is
119//! [`MessageId::SERVER_HELLO`], which is always sent in [`Mode::DEFAULT`] since
120//! the server has not heard from the client yet.
121//!
122//! # Negotiating the format
123//!
124//! A client picks the [`Format`] it wants to use, defaulting to
125//! [`Format::DEFAULT`]. The exchange is:
126//!
127//! 1. The server sends [`MessageId::SERVER_HELLO`] as soon as the connection is
128//! established. This carries no body.
129//! 2. The client responds with a [`MessageId::NEGOTIATE`] request whose
130//! envelope carries the desired [`Format`]. This carries no body.
131//! 3. If the server supports that format it records it for the connection and
132//! replies with an empty response whose envelope carries the format that was
133//! accepted. If it does not, it replies with an error listing the formats it
134//! does support, and the connection settles on [`Format::DEFAULT`].
135//!
136//! Both peers are forced through this before anything else can happen:
137//!
138//! * A client only reports itself as connected once step 3 has resolved.
139//! * A server has no way to write a message until then either, since
140//! [`ws::Connect::connect`] is what produces the [`ws::Server`] which can, and
141//! it does not resolve until step 3 has been flushed. Any other message
142//! arriving in place of step 2 is a protocol violation which closes the
143//! connection.
144//!
145//! Together this guarantees that server-initiated messages such as broadcasts
146//! are encoded with a format the client understands. The format is fixed for
147//! the lifetime of a connection, so a second negotiation is refused — a client
148//! which wants a different one reconnects.
149//!
150//! Requests additionally carry their own format in the envelope, so the server
151//! decodes each request body with the format that request declares and replies
152//! in the same format. Negotiation therefore only matters for messages the
153//! server sends on its own initiative.
154//!
155//! Since formats are gated behind [features], a server may genuinely be unable
156//! to speak a format a client asks for, which is why step 3 can fail.
157//!
158//! # Application versions
159//!
160//! [`VERSION`] only covers the envelope. The requests and broadcasts an
161//! application defines with [`define!`] are its own protocol, and a page which
162//! was loaded before the server was upgraded keeps speaking the old one for as
163//! long as it stays open. An upgrade safe [`Format`] tolerates added and
164//! missing fields, but not every change is like that.
165//!
166//! To find out, both peers can state an *application version* in the
167//! handshake: a number the application bumps whenever its API changes in a
168//! way an older peer cannot cope with, kept in a crate both sides depend on.
169//! The server states it with [`ws::Connect::app_version`] and a client with
170//! `ServiceBuilder::app_version`, in [`web`] or in [`client`].
171//!
172//! 1. The server states its version in the `app` header of
173//! [`MessageId::SERVER_HELLO`].
174//! 2. The client compares it with its own. It always sends its
175//! [`MessageId::NEGOTIATE`] request, stating its own version in the same
176//! header, so that the server can tell why it goes away.
177//! 3. If the two differ the client closes the connection and reports it as
178//! incompatible, with both versions, instead of opening it. It does not
179//! reconnect, since that reaches the same server. A page usually responds
180//! by reloading, which fetches the page the server is built with.
181//! 4. A server which is negotiated with by a client stating another version
182//! refuses it with a close frame, and [`ws::Connect::connect`] fails with an
183//! error for which [`ws::Error::is_incompatible`] holds.
184//!
185//! Zero means that no version is stated, and is what both sides default to.
186//! Versions are only compared when both peers state one, so a peer which does
187//! not opt in behaves exactly as it would without this, whatever the other
188//! one does. Every other message leaves the header at zero, which a binary
189//! envelope does not write at all.
190//!
191//! [`web`]: <https://docs.rs/musli-web/latest/musli_web/web/>
192//! [`client`]: <https://docs.rs/musli-web/latest/musli_web/client/>
193//! [`ws::Connect::app_version`]: <https://docs.rs/musli-web/latest/musli_web/ws/struct.Connect.html#method.app_version>
194//! [`ws::Error::is_incompatible`]: <https://docs.rs/musli-web/latest/musli_web/ws/struct.Error.html#method.is_incompatible>
195//!
196//! # Error responses
197//!
198//! A request which fails is answered with a response whose `error` header is
199//! non-zero, and whose body is encoded with the format the connection
200//! negotiated (rather than the one the request declared, since the request
201//! might have failed precisely because its format is not supported):
202//!
203//! * [`MessageId::ERROR_MESSAGE`] - the body is an [`ErrorMessage`], which
204//! carries a human readable message and nothing else. This is what the server
205//! sends when it fails a request on its own, such as one it does not support,
206//! and for a handler error which is plain text.
207//! * [`MessageId::TYPED_ERROR_MESSAGE`] - the body is an [`ErrorMessage`]
208//! followed by a second value, the *payload*, which is the error the handler
209//! returned encoded the same way a response body would be. The message is
210//! still there so that a client which does not know the type of the payload
211//! can show something.
212//!
213//! Any other non-zero value is an error the client has no message for, which
214//! it reports as an unsupported request (or broadcast).
215//!
216//! On the server a handler opts into the typed form by failing with a
217//! [`ws::TypedError`]. A client reads the payload back with
218//! `web::Error::server_error` or `client::Error::server_error`.
219//!
220//! [features]: <https://docs.rs/musli-web/latest/musli_web/#features>
221//! [`ws::Connect::connect`]: <https://docs.rs/musli-web/latest/musli_web/ws/struct.Connect.html#method.connect>
222//! [`ws::TypedError`]: <https://docs.rs/musli-web/latest/musli_web/ws/struct.TypedError.html>
223//! [`ws::Server`]: <https://docs.rs/musli-web/latest/musli_web/ws/struct.Server.html>
224
225use core::fmt;
226use core::num::NonZeroU16;
227use core::str::FromStr;
228use core::sync::atomic::{AtomicU16, Ordering};
229
230use musli::alloc::Global;
231use musli::mode::{Binary, Text};
232use musli::{Decode, Encode};
233
234#[doc(inline)]
235pub use musli_web_macros::define;
236
237/// The version of the wire protocol this build speaks.
238///
239/// It is stated by every envelope and checked by every peer, see [the protocol
240/// version]. A peer which states anything else is refused, since there is no
241/// way to know which parts of what it said still mean what they used to.
242///
243/// [the protocol version]: crate::api#the-protocol-version
244///
245/// # History
246///
247/// * `2` - a request may fail with a typed error, see [error responses], and
248/// the handshake carries an [application version].
249/// * `1` - the first versioned protocol.
250///
251/// [error responses]: crate::api#error-responses
252/// [application version]: crate::api#application-versions
253///
254/// # Examples
255///
256/// ```
257/// use musli_web::api::VERSION;
258///
259/// assert_eq!(VERSION, 2);
260/// ```
261pub const VERSION: u32 = 2;
262
263/// The serialization format used for message bodies.
264///
265/// The format is negotiated per connection, see the [negotiation protocol] for the
266/// details of how. Message *headers* are never affected by this and always use
267/// a fixed envelope, which is what makes negotiation possible in the first
268/// place.
269///
270/// The variants are ordered from least to most capable. Each capability that is
271/// dropped makes the encoding more compact:
272///
273/// | | `reorder` | `missing` | `unknown` | `self` |
274/// |-|-|-|-|-|
275/// | [`Packed`] | ✗ | ✗ | ✗ | ✗ |
276/// | [`Storage`] | ✔ | ✔ | ✗ | ✗ |
277/// | [`Wire`] | ✔ | ✔ | ✔ | ✗ |
278/// | [`Descriptive`] | ✔ | ✔ | ✔ | ✔ |
279/// | [`Json`] | ✔ | ✔ | ✔ | ✔ |
280///
281/// * `reorder` determines whether fields may be reordered in the model.
282/// * `missing` determines whether decoding tolerates missing fields, which is
283/// what allows new optional fields to be added.
284/// * `unknown` determines whether decoding can skip fields it does not know
285/// about. A format which can do this is *fully upgrade safe*, since an old
286/// peer can talk to a new one.
287/// * `self` determines whether the format is self-descriptive, so that the data
288/// can be decoded without the model.
289///
290/// [`Packed`]: Format::Packed
291/// [`Storage`]: Format::Storage
292/// [`Wire`]: Format::Wire
293/// [`Descriptive`]: Format::Descriptive
294/// [`Json`]: Format::Json
295///
296/// # Examples
297///
298/// ```
299/// use musli_web::api::Format;
300///
301/// assert_eq!(Format::default(), Format::Wire);
302/// assert!(Format::Wire.is_upgrade_safe());
303/// assert!(!Format::Storage.is_upgrade_safe());
304/// assert!(Format::Json.is_human_readable());
305/// ```
306#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
307#[non_exhaustive]
308pub enum Format {
309 /// The [`musli::packed`] format.
310 ///
311 /// The most compact format, but it requires that both peers use exactly the
312 /// same model. Suitable when client and server are deployed together.
313 Packed,
314 /// The [`musli::storage`] format.
315 ///
316 /// Tolerates missing fields, but cannot skip fields it does not know about.
317 Storage,
318 /// The [`musli::wire`] format.
319 ///
320 /// Fully upgrade safe, so peers built against different versions of the
321 /// model can talk to each other. This is the default.
322 Wire,
323 /// The [`musli::descriptive`] format.
324 ///
325 /// Fully upgrade safe and self-descriptive, at the cost of a larger
326 /// payload.
327 Descriptive,
328 /// The [`musli::json`] format.
329 ///
330 /// Human readable, which is useful when the traffic has to be inspected by
331 /// hand. Encoded using the [`Text`] mode so that fields are keyed by name.
332 ///
333 /// [`Text`]: musli::mode::Text
334 Json,
335}
336
337impl Format {
338 /// The default format, which is [`Format::Wire`].
339 ///
340 /// This is used by a client which has not picked a format, and by a server
341 /// for a connection which has not negotiated one.
342 ///
343 /// # Examples
344 ///
345 /// ```
346 /// use musli_web::api::Format;
347 ///
348 /// assert_eq!(Format::DEFAULT, Format::Wire);
349 /// ```
350 pub const DEFAULT: Self = Self::Wire;
351
352 /// Every format in order of increasing capability.
353 ///
354 /// Note that this includes formats which the crate might not have been
355 /// built with support for, see [`Format::is_supported`].
356 ///
357 /// # Examples
358 ///
359 /// ```
360 /// use musli_web::api::Format;
361 ///
362 /// assert!(Format::ALL.contains(&Format::Json));
363 /// ```
364 pub const ALL: &'static [Format] = &[
365 Format::Packed,
366 Format::Storage,
367 Format::Wire,
368 Format::Descriptive,
369 Format::Json,
370 ];
371
372 /// Get the stable identifier used for this format on the wire.
373 ///
374 /// Zero is never used, so it is available to indicate an absent format.
375 ///
376 /// # Examples
377 ///
378 /// ```
379 /// use musli_web::api::Format;
380 ///
381 /// assert_eq!(Format::Wire.to_u8(), 3);
382 /// assert_eq!(Format::from_u8(3), Some(Format::Wire));
383 /// ```
384 #[inline]
385 pub const fn to_u8(self) -> u8 {
386 match self {
387 Format::Packed => 1,
388 Format::Storage => 2,
389 Format::Wire => 3,
390 Format::Descriptive => 4,
391 Format::Json => 5,
392 }
393 }
394
395 /// Construct a format from the stable identifier used on the wire.
396 ///
397 /// Returns `None` if the identifier is not known, which is how a peer
398 /// built against an older version of this crate reports a format it has
399 /// never heard of.
400 ///
401 /// # Examples
402 ///
403 /// ```
404 /// use musli_web::api::Format;
405 ///
406 /// assert_eq!(Format::from_u8(1), Some(Format::Packed));
407 /// assert_eq!(Format::from_u8(0), None);
408 /// assert_eq!(Format::from_u8(200), None);
409 /// ```
410 #[inline]
411 pub const fn from_u8(id: u8) -> Option<Self> {
412 match id {
413 1 => Some(Format::Packed),
414 2 => Some(Format::Storage),
415 3 => Some(Format::Wire),
416 4 => Some(Format::Descriptive),
417 5 => Some(Format::Json),
418 _ => None,
419 }
420 }
421
422 /// The name of the format.
423 ///
424 /// # Examples
425 ///
426 /// ```
427 /// use musli_web::api::Format;
428 ///
429 /// assert_eq!(Format::Wire.name(), "wire");
430 /// ```
431 #[inline]
432 pub const fn name(self) -> &'static str {
433 match self {
434 Format::Packed => "packed",
435 Format::Storage => "storage",
436 Format::Wire => "wire",
437 Format::Descriptive => "descriptive",
438 Format::Json => "json",
439 }
440 }
441
442 /// Test if the format can skip over unknown fields, making it fully upgrade
443 /// safe.
444 ///
445 /// # Examples
446 ///
447 /// ```
448 /// use musli_web::api::Format;
449 ///
450 /// assert!(Format::Wire.is_upgrade_safe());
451 /// assert!(!Format::Packed.is_upgrade_safe());
452 /// ```
453 #[inline]
454 pub const fn is_upgrade_safe(self) -> bool {
455 matches!(self, Format::Wire | Format::Descriptive | Format::Json)
456 }
457
458 /// Test if the format is self-descriptive, so that data can be decoded
459 /// without access to the model.
460 ///
461 /// # Examples
462 ///
463 /// ```
464 /// use musli_web::api::Format;
465 ///
466 /// assert!(Format::Descriptive.is_self_descriptive());
467 /// assert!(!Format::Wire.is_self_descriptive());
468 /// ```
469 #[inline]
470 pub const fn is_self_descriptive(self) -> bool {
471 matches!(self, Format::Descriptive | Format::Json)
472 }
473
474 /// Test if the format produces output which is meant to be read by humans.
475 ///
476 /// # Examples
477 ///
478 /// ```
479 /// use musli_web::api::Format;
480 ///
481 /// assert!(Format::Json.is_human_readable());
482 /// assert!(!Format::Wire.is_human_readable());
483 /// ```
484 #[inline]
485 pub const fn is_human_readable(self) -> bool {
486 matches!(self, Format::Json)
487 }
488}
489
490impl Default for Format {
491 /// Construct the default format, which is [`Format::DEFAULT`].
492 ///
493 /// # Examples
494 ///
495 /// ```
496 /// use musli_web::api::Format;
497 ///
498 /// assert_eq!(Format::default(), Format::Wire);
499 /// ```
500 #[inline]
501 fn default() -> Self {
502 Self::DEFAULT
503 }
504}
505
506impl fmt::Display for Format {
507 #[inline]
508 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
509 f.write_str(self.name())
510 }
511}
512
513/// The error returned when parsing a [`Format`] from a string which is not the
514/// [`name`] of any format.
515///
516/// [`name`]: Format::name
517///
518/// # Examples
519///
520/// ```
521/// use musli_web::api::Format;
522///
523/// let error = "yaml".parse::<Format>().unwrap_err();
524/// assert_eq!(error.to_string(), "unknown format, expected one of: packed, storage, wire, descriptive, json");
525/// ```
526#[derive(Debug, Clone, Copy, PartialEq, Eq)]
527#[non_exhaustive]
528pub struct ParseFormatError;
529
530impl fmt::Display for ParseFormatError {
531 #[inline]
532 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
533 f.write_str("unknown format, expected one of: packed, storage, wire, descriptive, json")
534 }
535}
536
537impl core::error::Error for ParseFormatError {}
538
539impl FromStr for Format {
540 type Err = ParseFormatError;
541
542 /// Parse a format from exactly what its [`Display`] implementation and
543 /// [`Format::name`] produce.
544 ///
545 /// Matching is case sensitive and does not trim whitespace.
546 ///
547 /// [`Display`]: fmt::Display
548 ///
549 /// # Examples
550 ///
551 /// ```
552 /// use musli_web::api::Format;
553 ///
554 /// assert_eq!("wire".parse::<Format>(), Ok(Format::Wire));
555 /// assert!("Wire".parse::<Format>().is_err());
556 ///
557 /// for format in Format::ALL {
558 /// assert_eq!(format.to_string().parse::<Format>(), Ok(*format));
559 /// }
560 /// ```
561 fn from_str(s: &str) -> Result<Self, Self::Err> {
562 for &format in Self::ALL {
563 if format.name() == s {
564 return Ok(format);
565 }
566 }
567
568 Err(ParseFormatError)
569 }
570}
571
572/// How a socket frames the messages it carries.
573///
574/// This picks between the two shapes a message can have on the wire. It is
575/// orthogonal to the [`Format`] which bodies are encoded with, except that
576/// [`Mode::Text`] requires a human readable one, see the [text envelope].
577///
578/// [text envelope]: crate::api#the-text-envelope
579///
580/// # Examples
581///
582/// ```
583/// use musli_web::api::Mode;
584///
585/// assert_eq!(Mode::default(), Mode::Binary);
586/// assert!(Mode::Text.is_text());
587/// ```
588#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
589#[non_exhaustive]
590pub enum Mode {
591 /// Messages are binary frames whose envelope is encoded with
592 /// [`musli::packed`].
593 ///
594 /// This is the compact representation and the default.
595 Binary,
596 /// Messages are text frames whose envelope is a block of
597 /// `<key>: <value>` lines.
598 ///
599 /// The whole frame is human readable, which is what makes it possible to
600 /// read the traffic in a browser inspector or a proxy. Since the body is
601 /// part of that frame it has to be human readable too, so this requires a
602 /// [`Format`] for which [`Format::is_human_readable`] holds.
603 Text,
604}
605
606impl Mode {
607 /// The default mode, which is [`Mode::Binary`].
608 ///
609 /// This is used by a client which has not picked a mode, and by a server
610 /// for a connection which has not negotiated one.
611 ///
612 /// # Examples
613 ///
614 /// ```
615 /// use musli_web::api::Mode;
616 ///
617 /// assert_eq!(Mode::DEFAULT, Mode::Binary);
618 /// ```
619 pub const DEFAULT: Self = Self::Binary;
620
621 /// Every mode a socket can run in.
622 ///
623 /// # Examples
624 ///
625 /// ```
626 /// use musli_web::api::Mode;
627 ///
628 /// assert!(Mode::ALL.contains(&Mode::Text));
629 /// ```
630 pub const ALL: &'static [Mode] = &[Mode::Binary, Mode::Text];
631
632 /// The name of the mode.
633 ///
634 /// # Examples
635 ///
636 /// ```
637 /// use musli_web::api::Mode;
638 ///
639 /// assert_eq!(Mode::Text.name(), "text");
640 /// ```
641 #[inline]
642 pub const fn name(self) -> &'static str {
643 match self {
644 Mode::Binary => "binary",
645 Mode::Text => "text",
646 }
647 }
648
649 /// Test if the mode uses text frames.
650 ///
651 /// # Examples
652 ///
653 /// ```
654 /// use musli_web::api::Mode;
655 ///
656 /// assert!(Mode::Text.is_text());
657 /// assert!(!Mode::Binary.is_text());
658 /// ```
659 #[inline]
660 pub const fn is_text(self) -> bool {
661 matches!(self, Mode::Text)
662 }
663
664 /// Test if `format` can be used for message bodies in this mode.
665 ///
666 /// A text frame has to be valid UTF-8 in its entirety, so a mode which
667 /// uses them can only carry bodies which are human readable.
668 ///
669 /// # Examples
670 ///
671 /// ```
672 /// use musli_web::api::{Format, Mode};
673 ///
674 /// assert!(Mode::Binary.accepts(Format::Wire));
675 /// assert!(Mode::Text.accepts(Format::Json));
676 /// assert!(!Mode::Text.accepts(Format::Wire));
677 /// ```
678 #[inline]
679 pub const fn accepts(self, format: Format) -> bool {
680 match self {
681 Mode::Binary => true,
682 Mode::Text => format.is_human_readable(),
683 }
684 }
685
686 /// A dense index for the mode, so that it can be kept in an atomic.
687 ///
688 /// This is not a wire representation, the frame type is what carries the
689 /// mode between peers.
690 #[inline]
691 #[cfg_attr(not(feature = "client"), allow(dead_code))]
692 pub(crate) const fn to_index(self) -> u8 {
693 match self {
694 Mode::Binary => 0,
695 Mode::Text => 1,
696 }
697 }
698
699 /// Recover a mode from [`Mode::to_index`].
700 #[inline]
701 #[cfg_attr(not(feature = "client"), allow(dead_code))]
702 pub(crate) const fn from_index(index: u8) -> Option<Self> {
703 match index {
704 0 => Some(Mode::Binary),
705 1 => Some(Mode::Text),
706 _ => None,
707 }
708 }
709}
710
711impl Default for Mode {
712 /// Construct the default mode, which is [`Mode::DEFAULT`].
713 ///
714 /// # Examples
715 ///
716 /// ```
717 /// use musli_web::api::Mode;
718 ///
719 /// assert_eq!(Mode::default(), Mode::Binary);
720 /// ```
721 #[inline]
722 fn default() -> Self {
723 Self::DEFAULT
724 }
725}
726
727impl fmt::Display for Mode {
728 #[inline]
729 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
730 f.write_str(self.name())
731 }
732}
733
734/// The error returned when parsing a [`Mode`] from a string which is not the
735/// [`name`] of any mode.
736///
737/// [`name`]: Mode::name
738///
739/// # Examples
740///
741/// ```
742/// use musli_web::api::Mode;
743///
744/// let error = "both".parse::<Mode>().unwrap_err();
745/// assert_eq!(error.to_string(), "unknown mode, expected one of: binary, text");
746/// ```
747#[derive(Debug, Clone, Copy, PartialEq, Eq)]
748#[non_exhaustive]
749pub struct ParseModeError;
750
751impl fmt::Display for ParseModeError {
752 #[inline]
753 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
754 f.write_str("unknown mode, expected one of: binary, text")
755 }
756}
757
758impl core::error::Error for ParseModeError {}
759
760impl FromStr for Mode {
761 type Err = ParseModeError;
762
763 /// Parse a mode from exactly what its [`Display`] implementation and
764 /// [`Mode::name`] produce.
765 ///
766 /// Matching is case sensitive and does not trim whitespace.
767 ///
768 /// [`Display`]: fmt::Display
769 ///
770 /// # Examples
771 ///
772 /// ```
773 /// use musli_web::api::Mode;
774 ///
775 /// assert_eq!("text".parse::<Mode>(), Ok(Mode::Text));
776 /// assert!("Text".parse::<Mode>().is_err());
777 ///
778 /// for mode in Mode::ALL {
779 /// assert_eq!(mode.to_string().parse::<Mode>(), Ok(*mode));
780 /// }
781 /// ```
782 fn from_str(s: &str) -> Result<Self, Self::Err> {
783 for &mode in Self::ALL {
784 if mode.name() == s {
785 return Ok(mode);
786 }
787 }
788
789 Err(ParseModeError)
790 }
791}
792
793/// Types which can be encoded in every mode that this crate supports.
794///
795/// This is a blanket trait covering [`Encode`] in both the [`Binary`] and
796/// [`Text`] modes, which is what allows a message body to be encoded with any
797/// [`Format`] including [`Format::Json`].
798///
799/// Deriving [`Encode`] implements every mode, so this is implemented
800/// automatically unless the type has been restricted to a specific mode.
801pub trait EncodeBody
802where
803 Self: Encode<Binary> + Encode<Text>,
804{
805}
806
807impl<T> EncodeBody for T where T: ?Sized + Encode<Binary> + Encode<Text> {}
808
809/// Types which can be decoded in every mode that this crate supports.
810///
811/// This is a blanket trait covering [`Decode`] in both the [`Binary`] and
812/// [`Text`] modes, which is what allows a message body to be decoded with any
813/// [`Format`] including [`Format::Json`].
814///
815/// Deriving [`Decode`] implements every mode, so this is implemented
816/// automatically unless the type has been restricted to a specific mode.
817pub trait DecodeBody<'de>
818where
819 Self: Decode<'de, Binary, Global> + Decode<'de, Text, Global>,
820{
821}
822
823impl<'de, T> DecodeBody<'de> for T where T: Decode<'de, Binary, Global> + Decode<'de, Text, Global> {}
824
825/// A trait for constructing identifiers.
826///
827/// This is implemented by the `Request` and `Event` enums generated by
828/// [`define!`], and by [`ws::Either`] for a [`ws::Or`] handler.
829///
830/// [`ws::Either`]: <https://docs.rs/musli-web/latest/musli_web/ws/enum.Either.html>
831/// [`ws::Or`]: <https://docs.rs/musli-web/latest/musli_web/ws/struct.Or.html>
832pub trait Id
833where
834 Self: 'static + Send + Sized + fmt::Debug,
835{
836 /// The message ids this type knows.
837 ///
838 /// For a `Request` enum generated by [`define!`] these are the ids of its
839 /// endpoints, and for an `Event` enum those of its broadcasts, in both
840 /// cases along with those of every list it [includes]. An id outside of
841 /// this set is what [`Id::from_id`] constructs an unknown identifier for.
842 ///
843 /// [includes]: define#including-other-lists
844 ///
845 /// # Examples
846 ///
847 /// ```
848 /// use musli::{Decode, Encode};
849 /// use musli_web::api::{self, Id};
850 ///
851 /// #[derive(Encode, Decode)]
852 /// pub struct HelloRequest;
853 ///
854 /// #[derive(Encode, Decode)]
855 /// pub struct TickEvent;
856 ///
857 /// api::define! {
858 /// pub type Hello;
859 ///
860 /// impl Endpoint for Hello {
861 /// impl Request for HelloRequest;
862 /// type Response<'de> = ();
863 /// }
864 ///
865 /// pub type Tick;
866 ///
867 /// impl Broadcast for Tick {
868 /// impl Event for TickEvent;
869 /// }
870 /// }
871 ///
872 /// assert!(Request::IDS.contains(Hello::ID));
873 /// assert!(!Request::IDS.contains(Tick::ID));
874 /// assert!(Event::IDS.contains(Tick::ID));
875 /// ```
876 const IDS: IdSet;
877
878 /// Get the raw message identifier for this type.
879 fn id(&self) -> MessageId;
880
881 /// Construct an identifier from a raw message identifier.
882 ///
883 /// An id which is not in [`Id::IDS`] constructs an unknown identifier,
884 /// such as the `Unknown` variant of a `Request` enum generated by
885 /// [`define!`].
886 fn from_id(id: MessageId) -> Self;
887
888 /// Construct an identifier from a raw message identifier, or `None` if
889 /// the id is not in [`Id::IDS`].
890 ///
891 /// # Examples
892 ///
893 /// ```
894 /// use musli::{Decode, Encode};
895 /// use musli_web::api::{self, Id, MessageId};
896 ///
897 /// #[derive(Encode, Decode)]
898 /// pub struct HelloRequest;
899 ///
900 /// api::define! {
901 /// pub type Hello;
902 ///
903 /// impl Endpoint for Hello {
904 /// impl Request for HelloRequest;
905 /// type Response<'de> = ();
906 /// }
907 /// }
908 ///
909 /// assert_eq!(Request::try_from_id(Hello::ID), Some(Request::Hello));
910 ///
911 /// let unknown = MessageId::new(1000).unwrap();
912 /// assert_eq!(Request::try_from_id(unknown), None);
913 /// assert_eq!(Request::from_id(unknown), Request::Unknown(unknown));
914 /// ```
915 fn try_from_id(id: MessageId) -> Option<Self>;
916
917 #[doc(hidden)]
918 fn __do_not_implement_id();
919}
920
921/// A set of message ids, such as the ids an [`Id`] type knows, see
922/// [`Id::IDS`].
923///
924/// A set is built at compile time out of the ids of one [`define!`] list and
925/// the sets of the lists it includes, so it is cheap to construct but looking
926/// an id up in it is a linear scan. It is used to check, at compile time, that
927/// two lists which share a connection never use the same id.
928///
929/// # Examples
930///
931/// Checking in a test (or a `const` item) that two lists which are never
932/// included into one another, but are carried on the same connection, do not
933/// share an id:
934///
935/// ```
936/// use musli::{Decode, Encode};
937/// use musli_web::api::Id;
938///
939/// mod app {
940/// # use musli::{Decode, Encode};
941/// # use musli_web::api;
942/// #[derive(Encode, Decode)]
943/// pub struct HelloRequest;
944///
945/// api::define! {
946/// pub type Hello;
947///
948/// impl Endpoint for Hello {
949/// impl Request for HelloRequest;
950/// type Response<'de> = ();
951/// }
952/// }
953/// }
954///
955/// mod lib {
956/// # use musli::{Decode, Encode};
957/// # use musli_web::api;
958/// #[derive(Encode, Decode)]
959/// pub struct LoginRequest;
960///
961/// api::define! {
962/// #[musli(id = 32000)]
963/// pub type Login;
964///
965/// impl Endpoint for Login {
966/// impl Request for LoginRequest;
967/// type Response<'de> = ();
968/// }
969/// }
970/// }
971///
972/// const _: () = assert!(app::Request::IDS.is_disjoint(&lib::Request::IDS));
973/// assert_eq!(app::Request::IDS.first_shared(&lib::Request::IDS), None);
974/// ```
975#[derive(Clone, Copy)]
976pub struct IdSet {
977 #[doc(hidden)]
978 pub __ids: &'static [MessageId],
979 #[doc(hidden)]
980 pub __includes: &'static [&'static IdSet],
981}
982
983impl IdSet {
984 /// The empty set.
985 pub const EMPTY: Self = Self {
986 __ids: &[],
987 __includes: &[],
988 };
989
990 /// Test if the set contains `id`.
991 pub const fn contains(&self, id: MessageId) -> bool {
992 let mut n = 0;
993
994 while n < self.__ids.len() {
995 if self.__ids[n].get() == id.get() {
996 return true;
997 }
998
999 n += 1;
1000 }
1001
1002 let mut n = 0;
1003
1004 while n < self.__includes.len() {
1005 if self.__includes[n].contains(id) {
1006 return true;
1007 }
1008
1009 n += 1;
1010 }
1011
1012 false
1013 }
1014
1015 /// Test if the set has no id in common with `other`.
1016 #[inline]
1017 pub const fn is_disjoint(&self, other: &IdSet) -> bool {
1018 self.first_shared(other).is_none()
1019 }
1020
1021 /// Find the first id in this set which is also in `other`, if any.
1022 pub const fn first_shared(&self, other: &IdSet) -> Option<MessageId> {
1023 let mut n = 0;
1024
1025 while n < self.__ids.len() {
1026 let id = self.__ids[n];
1027
1028 if other.contains(id) {
1029 return Some(id);
1030 }
1031
1032 n += 1;
1033 }
1034
1035 let mut n = 0;
1036
1037 while n < self.__includes.len() {
1038 if let Some(id) = self.__includes[n].first_shared(other) {
1039 return Some(id);
1040 }
1041
1042 n += 1;
1043 }
1044
1045 None
1046 }
1047
1048 /// Test if the set is empty.
1049 pub const fn is_empty(&self) -> bool {
1050 if !self.__ids.is_empty() {
1051 return false;
1052 }
1053
1054 let mut n = 0;
1055
1056 while n < self.__includes.len() {
1057 if !self.__includes[n].is_empty() {
1058 return false;
1059 }
1060
1061 n += 1;
1062 }
1063
1064 true
1065 }
1066}
1067
1068impl fmt::Debug for IdSet {
1069 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1070 fn entries(set: &IdSet, list: &mut fmt::DebugSet<'_, '_>) {
1071 list.entries(set.__ids.iter().map(MessageId::get));
1072
1073 for include in set.__includes {
1074 entries(include, list);
1075 }
1076 }
1077
1078 let mut list = f.debug_set();
1079 entries(self, &mut list);
1080 list.finish()
1081 }
1082}
1083
1084/// A unique and opaque identifier for a channel over the websocket.
1085#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Encode, Decode)]
1086#[musli(transparent)]
1087pub struct ChannelId {
1088 repr: u16,
1089}
1090
1091impl ChannelId {
1092 /// The channel id used for an invalid channel.
1093 pub const NONE: Self = Self::from_u16(0);
1094
1095 /// Construct a new channel id from a raw `u16` representation.
1096 ///
1097 /// Note that this does not guarantee that the internal representation of a
1098 /// channel identifier is exactly a `u16`, only that at least `u16` unique
1099 /// identifiers can be constructed.
1100 ///
1101 /// Using `0` is equivalent to [`ChannelId::NONE`]. When implementing a
1102 /// custom [`ChannelAllocator`] the allocator must avoid constructor
1103 /// identifiers with this value since it is equivalent to no channel.
1104 ///
1105 /// [`ChannelAllocator`]: crate::ws::ChannelAllocator
1106 ///
1107 /// # Examples
1108 ///
1109 /// ```
1110 /// use musli_web::api::ChannelId;
1111 /// let id = ChannelId::from_u16(0);
1112 /// assert_eq!(id, ChannelId::NONE);
1113 /// ```
1114 #[inline]
1115 pub const fn from_u16(repr: u16) -> Self {
1116 Self { repr }
1117 }
1118
1119 /// Get the raw channel identifier.
1120 #[inline]
1121 #[cfg_attr(
1122 not(any(feature = "ws", feature = "client", feature = "web03")),
1123 allow(dead_code)
1124 )]
1125 pub(crate) const fn raw(&self) -> u16 {
1126 self.repr
1127 }
1128}
1129
1130impl fmt::Debug for ChannelId {
1131 #[inline]
1132 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1133 if self.repr == 0 {
1134 f.write_str("NONE")
1135 } else {
1136 write!(f, "{:04x}", self.repr)
1137 }
1138 }
1139}
1140
1141/// A [`ChannelId`] which can be shared and updated atomically.
1142///
1143/// This behaves like an atomic variable holding a [`ChannelId`], where the
1144/// value can be read, set, replaced, or taken. Each operation takes an
1145/// [`Ordering`] which is passed through to the underlying atomic.
1146///
1147/// [`Ordering`]: core::sync::atomic::Ordering
1148///
1149/// # Examples
1150///
1151/// ```
1152/// use core::sync::atomic::Ordering;
1153///
1154/// use musli_web::api::{AtomicChannelId, ChannelId};
1155///
1156/// let channel = AtomicChannelId::NONE;
1157/// assert_eq!(channel.load(Ordering::Acquire), ChannelId::NONE);
1158///
1159/// channel.store(ChannelId::from_u16(42), Ordering::Release);
1160/// assert_eq!(channel.load(Ordering::Acquire), ChannelId::from_u16(42));
1161///
1162/// assert_eq!(channel.take(Ordering::AcqRel), ChannelId::from_u16(42));
1163/// assert_eq!(channel.load(Ordering::Acquire), ChannelId::NONE);
1164/// ```
1165#[repr(transparent)]
1166pub struct AtomicChannelId {
1167 repr: AtomicU16,
1168}
1169
1170impl AtomicChannelId {
1171 /// An atomic channel id which contains [`ChannelId::NONE`].
1172 ///
1173 /// Since this is a constant every use of it constructs a new value, to
1174 /// share one it has to be bound to a `static` or a variable.
1175 ///
1176 /// # Examples
1177 ///
1178 /// ```
1179 /// use core::sync::atomic::Ordering;
1180 ///
1181 /// use musli_web::api::{AtomicChannelId, ChannelId};
1182 ///
1183 /// let channel = AtomicChannelId::NONE;
1184 /// assert_eq!(channel.load(Ordering::Acquire), ChannelId::NONE);
1185 /// ```
1186 #[allow(clippy::declare_interior_mutable_const)]
1187 pub const NONE: Self = Self::new(ChannelId::NONE);
1188
1189 /// Construct a new atomic channel id containing `id`.
1190 ///
1191 /// # Examples
1192 ///
1193 /// ```
1194 /// use core::sync::atomic::Ordering;
1195 ///
1196 /// use musli_web::api::{AtomicChannelId, ChannelId};
1197 ///
1198 /// let channel = AtomicChannelId::new(ChannelId::from_u16(1));
1199 /// assert_eq!(channel.load(Ordering::Acquire), ChannelId::from_u16(1));
1200 /// ```
1201 #[inline]
1202 pub const fn new(id: ChannelId) -> Self {
1203 Self {
1204 repr: AtomicU16::new(id.repr),
1205 }
1206 }
1207
1208 /// Read the current channel id.
1209 ///
1210 /// `ordering` describes the memory ordering of this operation. Possible
1211 /// values are [`SeqCst`], [`Acquire`] and [`Relaxed`].
1212 ///
1213 /// [`SeqCst`]: Ordering::SeqCst
1214 /// [`Acquire`]: Ordering::Acquire
1215 /// [`Relaxed`]: Ordering::Relaxed
1216 ///
1217 /// # Panics
1218 ///
1219 /// Panics if `ordering` is [`Release`] or [`AcqRel`].
1220 ///
1221 /// [`Release`]: Ordering::Release
1222 /// [`AcqRel`]: Ordering::AcqRel
1223 ///
1224 /// # Examples
1225 ///
1226 /// ```
1227 /// use core::sync::atomic::Ordering;
1228 ///
1229 /// use musli_web::api::{AtomicChannelId, ChannelId};
1230 ///
1231 /// let channel = AtomicChannelId::new(ChannelId::from_u16(1));
1232 /// assert_eq!(channel.load(Ordering::Acquire), ChannelId::from_u16(1));
1233 /// ```
1234 #[inline]
1235 pub fn load(&self, ordering: Ordering) -> ChannelId {
1236 ChannelId::from_u16(self.repr.load(ordering))
1237 }
1238
1239 /// Set the current channel id to `id`, discarding the old value.
1240 ///
1241 /// `ordering` describes the memory ordering of this operation. Possible
1242 /// values are [`SeqCst`], [`Release`] and [`Relaxed`].
1243 ///
1244 /// [`SeqCst`]: Ordering::SeqCst
1245 /// [`Release`]: Ordering::Release
1246 /// [`Relaxed`]: Ordering::Relaxed
1247 ///
1248 /// # Panics
1249 ///
1250 /// Panics if `ordering` is [`Acquire`] or [`AcqRel`].
1251 ///
1252 /// [`Acquire`]: Ordering::Acquire
1253 /// [`AcqRel`]: Ordering::AcqRel
1254 ///
1255 /// # Examples
1256 ///
1257 /// ```
1258 /// use core::sync::atomic::Ordering;
1259 ///
1260 /// use musli_web::api::{AtomicChannelId, ChannelId};
1261 ///
1262 /// let channel = AtomicChannelId::NONE;
1263 /// channel.store(ChannelId::from_u16(1), Ordering::Release);
1264 /// assert_eq!(channel.load(Ordering::Acquire), ChannelId::from_u16(1));
1265 /// ```
1266 #[inline]
1267 pub fn store(&self, id: ChannelId, ordering: Ordering) {
1268 self.repr.store(id.repr, ordering);
1269 }
1270
1271 /// Replace the current channel id with `id`, returning the old value.
1272 ///
1273 /// `ordering` describes the memory ordering of this operation. All
1274 /// orderings are possible. Note that using [`Acquire`] makes the store part
1275 /// of this operation [`Relaxed`], and using [`Release`] makes the load part
1276 /// [`Relaxed`].
1277 ///
1278 /// [`Acquire`]: Ordering::Acquire
1279 /// [`Release`]: Ordering::Release
1280 /// [`Relaxed`]: Ordering::Relaxed
1281 ///
1282 /// # Examples
1283 ///
1284 /// ```
1285 /// use core::sync::atomic::Ordering;
1286 ///
1287 /// use musli_web::api::{AtomicChannelId, ChannelId};
1288 ///
1289 /// let channel = AtomicChannelId::new(ChannelId::from_u16(1));
1290 ///
1291 /// let old = channel.replace(ChannelId::from_u16(2), Ordering::AcqRel);
1292 /// assert_eq!(old, ChannelId::from_u16(1));
1293 /// assert_eq!(channel.load(Ordering::Acquire), ChannelId::from_u16(2));
1294 /// ```
1295 #[inline]
1296 pub fn replace(&self, id: ChannelId, ordering: Ordering) -> ChannelId {
1297 ChannelId::from_u16(self.repr.swap(id.repr, ordering))
1298 }
1299
1300 /// Take the current channel id, leaving [`ChannelId::NONE`] in its place.
1301 ///
1302 /// `ordering` describes the memory ordering of this operation. All
1303 /// orderings are possible, see [`replace`] for details.
1304 ///
1305 /// [`replace`]: AtomicChannelId::replace
1306 ///
1307 /// # Examples
1308 ///
1309 /// ```
1310 /// use core::sync::atomic::Ordering;
1311 ///
1312 /// use musli_web::api::{AtomicChannelId, ChannelId};
1313 ///
1314 /// let channel = AtomicChannelId::new(ChannelId::from_u16(1));
1315 ///
1316 /// assert_eq!(channel.take(Ordering::AcqRel), ChannelId::from_u16(1));
1317 /// assert_eq!(channel.load(Ordering::Acquire), ChannelId::NONE);
1318 /// assert_eq!(channel.take(Ordering::AcqRel), ChannelId::NONE);
1319 /// ```
1320 #[inline]
1321 pub fn take(&self, ordering: Ordering) -> ChannelId {
1322 self.replace(ChannelId::NONE, ordering)
1323 }
1324
1325 /// Consume the atomic channel id, returning the contained value.
1326 ///
1327 /// Since this takes ownership no synchronization is needed.
1328 ///
1329 /// # Examples
1330 ///
1331 /// ```
1332 /// use musli_web::api::{AtomicChannelId, ChannelId};
1333 ///
1334 /// let channel = AtomicChannelId::new(ChannelId::from_u16(1));
1335 /// assert_eq!(channel.into_inner(), ChannelId::from_u16(1));
1336 /// ```
1337 #[inline]
1338 pub fn into_inner(self) -> ChannelId {
1339 ChannelId::from_u16(self.repr.into_inner())
1340 }
1341}
1342
1343impl Default for AtomicChannelId {
1344 /// Construct an atomic channel id containing [`ChannelId::NONE`].
1345 ///
1346 /// # Examples
1347 ///
1348 /// ```
1349 /// use core::sync::atomic::Ordering;
1350 ///
1351 /// use musli_web::api::{AtomicChannelId, ChannelId};
1352 ///
1353 /// let channel = AtomicChannelId::default();
1354 /// assert_eq!(channel.load(Ordering::Acquire), ChannelId::NONE);
1355 /// ```
1356 #[inline]
1357 fn default() -> Self {
1358 Self::NONE
1359 }
1360}
1361
1362impl From<ChannelId> for AtomicChannelId {
1363 #[inline]
1364 fn from(id: ChannelId) -> Self {
1365 Self::new(id)
1366 }
1367}
1368
1369impl fmt::Debug for AtomicChannelId {
1370 #[inline]
1371 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1372 self.load(Ordering::Relaxed).fmt(f)
1373 }
1374}
1375
1376/// A raw identifier for a message.
1377#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Encode, Decode)]
1378#[repr(transparent)]
1379#[musli(transparent)]
1380pub struct MessageId(NonZeroU16);
1381
1382impl fmt::Display for MessageId {
1383 #[inline]
1384 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1385 self.0.fmt(f)
1386 }
1387}
1388
1389impl MessageId {
1390 /// The error id of a response whose body is an [`ErrorMessage`].
1391 ///
1392 /// See [error responses](crate::api#error-responses).
1393 pub const ERROR_MESSAGE: Self = unsafe { Self::new_unchecked((i16::MAX as u16) + 1) };
1394
1395 /// A connect of a channel.
1396 pub const CONNECT: Self = unsafe { Self::new_unchecked((i16::MAX as u16) + 2) };
1397
1398 /// A clean disconnect of a channel.
1399 pub const DISCONNECT: Self = unsafe { Self::new_unchecked((i16::MAX as u16) + 3) };
1400
1401 /// The first message the server sends to indicat that a connection is open.
1402 pub const SERVER_HELLO: Self = unsafe { Self::new_unchecked((i16::MAX as u16) + 4) };
1403
1404 /// A request from the client to use a particular [`Format`] for the
1405 /// remainder of the connection.
1406 ///
1407 /// See the [negotiation protocol] for how this is used.
1408 pub const NEGOTIATE: Self = unsafe { Self::new_unchecked((i16::MAX as u16) + 5) };
1409
1410 /// The error id of a response whose body is an [`ErrorMessage`] followed by
1411 /// a typed payload.
1412 ///
1413 /// See [error responses](crate::api#error-responses).
1414 pub const TYPED_ERROR_MESSAGE: Self = unsafe { Self::new_unchecked((i16::MAX as u16) + 6) };
1415
1416 /// The message id for an empty packet constructed using [`Packet::empty`]
1417 /// or [`RawPacket::empty`].
1418 ///
1419 /// [`Packet::empty`]: crate::web::Packet::empty
1420 /// [`RawPacket::empty`]: crate::web::RawPacket::empty
1421 ///
1422 /// # Examples
1423 ///
1424 /// ```
1425 /// use musli_web::api::MessageId;
1426 /// use musli_web::web::{RawPacket, Packet};
1427 ///
1428 /// let packet = RawPacket::empty();
1429 /// assert_eq!(packet.id(), MessageId::EMPTY);
1430 ///
1431 /// let packet = Packet::<()>::empty();
1432 /// assert_eq!(packet.id(), MessageId::EMPTY);
1433 /// ```
1434 pub const EMPTY: Self = unsafe { Self::new_unchecked(u16::MAX) };
1435
1436 /// Construct a raw message id.
1437 #[inline]
1438 pub const fn new(id: u16) -> Option<Self> {
1439 let Some(value) = NonZeroU16::new(id) else {
1440 return None;
1441 };
1442
1443 Some(Self(value))
1444 }
1445
1446 /// Get a raw message identifier.
1447 #[inline]
1448 pub const fn get(&self) -> u16 {
1449 self.0.get()
1450 }
1451
1452 /// Construct a new message ID.
1453 ///
1454 /// # Safety
1455 ///
1456 /// The caller must ensure that the provided `id` is non-zero.
1457 #[inline]
1458 pub const unsafe fn new_unchecked(id: u16) -> Self {
1459 Self(unsafe { NonZeroU16::new_unchecked(id) })
1460 }
1461}
1462
1463/// A trait implemented for types which can be decoded into something.
1464///
1465/// Do not implement manually, instead use the [`define!`] macro.
1466pub trait Decodable {
1467 /// The decodable type related to this.
1468 type Type<'de>: DecodeBody<'de>;
1469
1470 #[doc(hidden)]
1471 fn __do_not_implement_decodable();
1472}
1473
1474/// An endpoint marker trait.
1475///
1476/// Do not implement manually, instead use the [`define!`] macro.
1477pub trait Endpoint
1478where
1479 Self: 'static,
1480 for<'de> Self: Decodable<Type<'de> = Self::Response<'de>>,
1481{
1482 /// The kind of the endpoint.
1483 const ID: MessageId;
1484
1485 /// The primary response type related to the endpoint.
1486 type Response<'de>: DecodeBody<'de>;
1487
1488 #[doc(hidden)]
1489 fn __do_not_implement_endpoint();
1490}
1491
1492/// The marker trait used for broadcasts.
1493///
1494/// Do not implement manually, instead use the [`define!`] macro.
1495pub trait Broadcast
1496where
1497 Self: 'static,
1498{
1499 /// The kind of the broadcast.
1500 const ID: MessageId;
1501
1502 #[doc(hidden)]
1503 fn __do_not_implement_broadcast();
1504}
1505
1506/// Trait implemented for broadcasts which have a primary event.
1507pub trait BroadcastWithEvent
1508where
1509 Self: Broadcast,
1510 for<'de> Self: Decodable<Type<'de> = Self::Event<'de>>,
1511{
1512 /// The event type related to the broadcast.
1513 type Event<'de>: Event<Broadcast = Self> + DecodeBody<'de>
1514 where
1515 Self: 'de;
1516
1517 #[doc(hidden)]
1518 fn __do_not_implement_broadcast_with_event();
1519}
1520
1521/// A marker indicating a request type.
1522///
1523/// Do not implement manually, instead use the [`define!`] macro.
1524pub trait Request
1525where
1526 Self: EncodeBody,
1527{
1528 /// The endpoint related to the request.
1529 type Endpoint: Endpoint;
1530
1531 #[doc(hidden)]
1532 fn __do_not_implement_request();
1533}
1534
1535/// The event of a broadcast.
1536///
1537/// Do not implement manually, instead use the [`define!`] macro.
1538pub trait Event
1539where
1540 Self: EncodeBody,
1541{
1542 /// The endpoint related to the broadcast.
1543 type Broadcast: Broadcast;
1544
1545 #[doc(hidden)]
1546 fn __do_not_implement_event();
1547}
1548
1549/// A request to connect.
1550#[derive(Debug, Clone, Copy, Encode, Decode)]
1551#[doc(hidden)]
1552#[musli(packed)]
1553pub struct Connect;
1554
1555/// The header of a response.
1556///
1557/// This is the envelope of every message the server sends, see the [wire
1558/// format].
1559///
1560/// [wire format]: crate::api#wire-format
1561#[derive(Debug, Clone)]
1562#[doc(hidden)]
1563pub struct ResponseHeader {
1564 /// The version of the protocol this envelope was written against, which is
1565 /// always [`VERSION`].
1566 pub version: u32,
1567 /// The serial request this is a response to.
1568 pub serial: u32,
1569 /// This is a broadcast over the specified type. If this is non-empty the
1570 /// serial is 0.
1571 pub broadcast: u16,
1572 /// If non-zero, the response is an error and this says what its body
1573 /// carries, see [error responses](crate::api#error-responses).
1574 pub error: u16,
1575 /// The [`Format`] the body of this response is encoded with, as given by
1576 /// [`Format::to_u8`]. Zero if the response carries no body.
1577 pub format: u8,
1578 /// The channel over which the response will be sent.
1579 pub channel: ChannelId,
1580 /// The application version the server states, see [application
1581 /// versions]. Only [`MessageId::SERVER_HELLO`] carries it, and zero means
1582 /// that none is stated.
1583 ///
1584 /// [application versions]: crate::api#application-versions
1585 pub app: u32,
1586}
1587
1588/// The body of an error response.
1589///
1590/// When the response carries [`MessageId::TYPED_ERROR_MESSAGE`] the typed
1591/// payload follows it in the same body, see [error
1592/// responses](crate::api#error-responses).
1593#[derive(Debug, Clone, Encode, Decode)]
1594#[doc(hidden)]
1595#[musli(packed)]
1596pub struct ErrorMessage<'de> {
1597 /// The error message.
1598 pub message: &'de str,
1599}
1600
1601/// A request header.
1602///
1603/// This is the envelope of every message the client sends, see the [wire
1604/// format].
1605///
1606/// [wire format]: crate::api#wire-format
1607#[derive(Debug, Clone, Copy)]
1608#[doc(hidden)]
1609pub struct RequestHeader {
1610 /// The version of the protocol this envelope was written against, which is
1611 /// always [`VERSION`].
1612 pub version: u32,
1613 /// The serial of the request.
1614 pub serial: u32,
1615 /// The kind of the request.
1616 pub id: u16,
1617 /// The [`Format`] the body of this request is encoded with, as given by
1618 /// [`Format::to_u8`]. Zero if the request carries no body.
1619 pub format: u8,
1620 /// The channel over which the request was received.
1621 pub channel: ChannelId,
1622 /// The application version the client states, see [application
1623 /// versions]. Only [`MessageId::NEGOTIATE`] carries it, and zero means
1624 /// that none is stated.
1625 ///
1626 /// [application versions]: crate::api#application-versions
1627 pub app: u32,
1628}
1629
1630#[cfg(test)]
1631mod from_str_tests {
1632 use crate::std::string::ToString;
1633
1634 use super::{Format, Mode, ParseFormatError, ParseModeError};
1635
1636 #[test]
1637 fn format_round_trips() {
1638 for &format in Format::ALL {
1639 assert_eq!(format.name().parse::<Format>(), Ok(format));
1640 assert_eq!(format.to_string().parse::<Format>(), Ok(format));
1641 }
1642 }
1643
1644 #[test]
1645 fn mode_round_trips() {
1646 for &mode in Mode::ALL {
1647 assert_eq!(mode.name().parse::<Mode>(), Ok(mode));
1648 assert_eq!(mode.to_string().parse::<Mode>(), Ok(mode));
1649 }
1650 }
1651
1652 #[test]
1653 fn format_rejects() {
1654 for s in [
1655 "", " wire", "wire ", "Wire", "WIRE", "json\n", "binary", "w", "wire\0",
1656 ] {
1657 assert_eq!(s.parse::<Format>(), Err(ParseFormatError), "{s:?}");
1658 }
1659 }
1660
1661 #[test]
1662 fn mode_rejects() {
1663 for s in [
1664 "", " text", "text ", "Text", "BINARY", "wire", "bin", "text\0",
1665 ] {
1666 assert_eq!(s.parse::<Mode>(), Err(ParseModeError), "{s:?}");
1667 }
1668 }
1669}