musli_web/lib.rs
1//! [<img alt="github" src="https://img.shields.io/badge/github-udoprog/musli-8da0cb?style=for-the-badge&logo=github" height="20">](https://github.com/udoprog/musli)
2//! [<img alt="crates.io" src="https://img.shields.io/crates/v/musli-web.svg?style=for-the-badge&color=fc8d62&logo=rust" height="20">](https://crates.io/crates/musli-web)
3//! [<img alt="docs.rs" src="https://img.shields.io/badge/docs.rs-musli--web-66c2a5?style=for-the-badge&logoColor=white&logo=data:image/svg+xml;base64,PHN2ZyByb2xlPSJpbWciIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgdmlld0JveD0iMCAwIDUxMiA1MTIiPjxwYXRoIGZpbGw9IiNmNWY1ZjUiIGQ9Ik00ODguNiAyNTAuMkwzOTIgMjE0VjEwNS41YzAtMTUtOS4zLTI4LjQtMjMuNC0zMy43bC0xMDAtMzcuNWMtOC4xLTMuMS0xNy4xLTMuMS0yNS4zIDBsLTEwMCAzNy41Yy0xNC4xIDUuMy0yMy40IDE4LjctMjMuNCAzMy43VjIxNGwtOTYuNiAzNi4yQzkuMyAyNTUuNSAwIDI2OC45IDAgMjgzLjlWMzk0YzAgMTMuNiA3LjcgMjYuMSAxOS45IDMyLjJsMTAwIDUwYzEwLjEgNS4xIDIyLjEgNS4xIDMyLjIgMGwxMDMuOS01MiAxMDMuOSA1MmMxMC4xIDUuMSAyMi4xIDUuMSAzMi4yIDBsMTAwLTUwYzEyLjItNi4xIDE5LjktMTguNiAxOS45LTMyLjJWMjgzLjljMC0xNS05LjMtMjguNC0yMy40LTMzLjd6TTM1OCAyMTQuOGwtODUgMzEuOXYtNjguMmw4NS0zN3Y3My4zek0xNTQgMTA0LjFsMTAyLTM4LjIgMTAyIDM4LjJ2LjZsLTEwMiA0MS40LTEwMi00MS40di0uNnptODQgMjkxLjFsLTg1IDQyLjV2LTc5LjFsODUtMzguOHY3NS40em0wLTExMmwtMTAyIDQxLjQtMTAyLTQxLjR2LS42bDEwMi0zOC4yIDEwMiAzOC4ydi42em0yNDAgMTEybC04NSA0Mi41di03OS4xbDg1LTM4Ljh2NzUuNHptMC0xMTJsLTEwMiA0MS40LTEwMi00MS40di0uNmwxMDItMzguMiAxMDIgMzguMnYuNnoiPjwvcGF0aD48L3N2Zz4K" height="20">](https://docs.rs/musli-web)
4//!
5//! This crate provides a set of utilities for working with various web-based
6//! APIs and [Müsli].
7//!
8//! It includes support for:
9//! - [`axum`] [`Json`] integration, allowing you to use Müsli for serialization
10//! and deserialization in your Axum applications.
11//! - [`axum`] [`ws::Server`] integration, allowing you to build the server side
12//! of the websocket protocol this crate implements.
13//! - [`yew`] integration, allowing you to use Müsli for communicating with
14//! websocket clients using a well-defined API.
15//! - [`tokio-tungstenite`] integration through [`tungstenite029`] and
16//! [`tungstenite030`], allowing non-browser clients to talk to the same
17//! websocket API.
18//!
19//! Note that the organization of the modules include the version of the corresponding
20//! crate. Unstable versions are prefixed with `0`, such as [`yew023`].
21//!
22//! See the following modules for how to use:
23//! * [`axum08`] for [`axum`] `0.8.x` integration.
24//! * [`tungstenite029`] for [`tokio-tungstenite`] `0.29.x` integration.
25//! * [`tungstenite030`] for [`tokio-tungstenite`] `0.30.x` integration.
26//! * [`yew022`] for [`yew`] `0.22.x` integration.
27//! * [`yew023`] for [`yew`] `0.23.x` integration.
28//! * [`web03`] for [`web-sys`] `0.3.x` integration.
29//!
30//! <br>
31//!
32//! ## Features
33//!
34//! * A wasm-compatible and convenient websocket protocol with support for
35//! request-reply and broadcasts.
36//! * [Channel support] allowing the server to identify the source of a message
37//! and clients to correlate messages.
38//! * [Typed request errors]. A handler can fail a request with an error the
39//! client decodes back into its type, rather than a message it has to parse.
40//! * A [negotiable body format], so a client can pick how much schema evolution
41//! it needs and the server adapts to it. Message envelopes never depend on
42//! the outcome, so negotiation itself is always readable.
43//! * A [selectable socket mode], so a connection can be switched from binary
44//! frames to text ones whose envelope is a block of `<key>: <value>` lines.
45//! Together with [`Format::Json`] this makes the whole conversation readable
46//! in a proxy or a browser inspector, which is what it is for.
47//! * An [explicitly versioned] protocol. Every message states the [`VERSION`]
48//! it was written against and a header which is not recognized is refused
49//! rather than skipped, so peers which do not speak the same protocol part
50//! ways before either has acted on what the other said.
51//! * An optional [application version] in the handshake, so a page which was
52//! loaded before the server was upgraded learns that it has to reload instead
53//! of talking to an API which has moved on.
54//!
55//! The available formats, from most compact to most capable, are
56//! [`Format::Packed`], [`Format::Storage`], [`Format::Wire`] (the default, and
57//! the least capable one which is fully upgrade safe), [`Format::Descriptive`]
58//! and [`Format::Json`]. Each is gated behind a `format-*` feature.
59//!
60//! <br>
61//!
62//! ## Choosing a format
63//!
64//! The body of every message is encoded with one format per connection. The
65//! envelope around it never depends on the choice, so the two sides can always
66//! talk long enough to settle it.
67//!
68//! * The client asks. Call `format` on the service builder (`ServiceBuilder`
69//! in the browser and `client::ServiceBuilder` for native clients) with a
70//! `Format`. A client which says nothing speaks `Format::Wire`, which tolerates
71//! fields being added, removed and unknown, so client and server can be
72//! deployed separately. `Format` implements `FromStr` and `Display`, so a
73//! choice can come from a configuration value or a query string, as in
74//! `"json".parse::<Format>()`.
75//! * The server accepts. By default it accepts every format it was built with.
76//! Call `with_formats` on the server to narrow that to a list, such as
77//! `.with_formats(&[Format::Wire, Format::Json])`. This can never add a format
78//! which was not built in.
79//! * A request for a format the server does not accept is reported to the client
80//! through its `on_error` callback and the connection continues with
81//! `Format::Wire`. The format in use can be read back with `format()` on the
82//! client's handle. Typed request errors are encoded in the connection's
83//! format too.
84//! * A client that requests `Format::Json` can also ask for text frames with
85//! `mode(Mode::Text)`, which makes the whole conversation readable in a
86//! browser inspector. Text frames require a human readable format.
87//! * The application version of the handshake is independent of the format. It
88//! is what tells an old page that the server has moved on.
89//!
90//! A format is only available if its `format-*` feature is enabled, in the
91//! crate that performs the encoding. `format-wire` is on wherever `api` is, so
92//! `Format::Wire` is always available.
93//!
94//! | Role | Features to enable | Notes |
95//! |-|-|-|
96//! | Shared api crate | `api` | Enables `format-wire`. Nothing else is needed to define requests and broadcasts. |
97//! | Server | `axum08` | Enables `format-storage` and `format-wire`. Add `format-descriptive`, `format-json` or `format-packed` for each further format it should accept. |
98//! | Browser client | `yew022`, `yew023` or `web03` | Enables `format-storage` and `format-wire`. Add the `format-*` feature of any other format it selects. |
99//! | Native client | `tungstenite029` or `tungstenite030` | Enables `format-storage` and `format-wire`. Add the `format-*` feature of any other format it selects. |
100//!
101//! Only enable what is used. A browser client which never calls `format` speaks
102//! `Format::Wire` and needs no `format-*` feature of its own, and a server which
103//! only serves such clients does not need `format-descriptive` or `format-json`.
104//! Cargo unifies features across a build, so in a workspace a `format-*` feature
105//! enabled by one crate is on for every crate that uses `musli-web`. Check what
106//! each crate really selects.
107//!
108//! The `json` feature is unrelated: it provides the `Json` extractor for `axum`
109//! and does not enable `format-json`. The `formats` feature enables every
110//! `format-*` feature at once.
111//!
112//! [Channel support]: https://docs.rs/musli-web/latest/musli_web/web/struct.Handle.html#method.channel
113//! [negotiable body format]: https://docs.rs/musli-web/latest/musli_web/api/index.html#wire-format
114//! [selectable socket mode]: https://docs.rs/musli-web/latest/musli_web/api/index.html#the-text-envelope
115//! [explicitly versioned]: https://docs.rs/musli-web/latest/musli_web/api/index.html#the-protocol-version
116//! [application version]: https://docs.rs/musli-web/latest/musli_web/api/index.html#application-versions
117//! [Typed request errors]: https://docs.rs/musli-web/latest/musli_web/ws/index.html#errors
118//!
119//! <br>
120//!
121//! ## Examples
122//!
123//! * [`api`] is the example crate which defines API types shared between server
124//! and client.
125//! * [`server`] is the axum-based server implementation.
126//! * [`client`] is the yew client communicating with the server.
127//!
128//! You can run the client like this:
129//!
130//! ```sh
131//! cd examples/client && trunk serve
132//! ```
133//!
134//! You can run the server like this:
135//!
136//! ```sh
137//! cd examples/server && cargo run
138//! ```
139//!
140//! [`api`]: <https://github.com/udoprog/musli/tree/main/crates/musli-web/examples/api/>
141//! [`axum`]: <https://docs.rs/axum>
142//! [`client`]: <https://github.com/udoprog/musli/tree/main/crates/musli-web/examples/client/>
143//! [`Json`]: <https://docs.rs/musli-web/latest/musli-web/Json.struct.html>
144//! [`server`]: <https://github.com/udoprog/musli/tree/main/crates/musli-web/examples/server/>
145//! [`tokio-tungstenite`]: <https://docs.rs/tokio-tungstenite>
146//! [`web-sys`]: <https://docs.rs/web-sys>
147//! [`ws::Server`]: <https://docs.rs/musli-web/latest/musli_web/ws/struct.Server.html>
148//! [`yew`]: <https://yew.rs>
149//! [Müsli]: <https://github.com/udoprog/musli>
150
151#![no_std]
152#![cfg_attr(doc_cfg, feature(doc_cfg))]
153#![allow(clippy::type_complexity)]
154
155#[cfg(feature = "std")]
156extern crate std;
157
158#[cfg(feature = "alloc")]
159extern crate alloc;
160
161#[cfg(feature = "axum08")]
162mod buf;
163#[cfg(feature = "axum08")]
164use self::buf::Buf;
165
166#[cfg(feature = "json")]
167mod json;
168#[cfg(feature = "json")]
169#[cfg_attr(doc_cfg, doc(cfg(feature = "json")))]
170pub use self::json::Json;
171
172#[cfg(feature = "api")]
173#[cfg_attr(doc_cfg, doc(cfg(feature = "api")))]
174pub mod api;
175#[doc(inline)]
176#[cfg(feature = "api")]
177#[cfg_attr(doc_cfg, doc(cfg(feature = "api")))]
178pub use self::api::{AtomicChannelId, ChannelId, Format, Mode, VERSION};
179
180#[macro_use]
181mod macros;
182
183#[cfg(feature = "api")]
184#[cfg_attr(doc_cfg, doc(cfg(feature = "api")))]
185pub mod format;
186
187#[cfg(feature = "axum08")]
188#[cfg_attr(doc_cfg, doc(cfg(feature = "axum08")))]
189pub mod axum08;
190
191#[cfg(feature = "client")]
192#[cfg_attr(doc_cfg, doc(cfg(feature = "client")))]
193pub mod client;
194
195#[cfg(feature = "tungstenite029")]
196#[cfg_attr(doc_cfg, doc(cfg(feature = "tungstenite029")))]
197pub mod tungstenite029;
198
199#[cfg(feature = "tungstenite030")]
200#[cfg_attr(doc_cfg, doc(cfg(feature = "tungstenite030")))]
201pub mod tungstenite030;
202
203#[cfg(feature = "web03")]
204#[cfg_attr(doc_cfg, doc(cfg(feature = "web03")))]
205pub mod web;
206
207#[cfg(feature = "web03")]
208#[cfg_attr(doc_cfg, doc(cfg(feature = "web03")))]
209pub mod web03;
210
211#[cfg(feature = "yew022")]
212#[cfg_attr(doc_cfg, doc(cfg(feature = "yew022")))]
213pub mod yew022;
214
215#[cfg(feature = "yew023")]
216#[cfg_attr(doc_cfg, doc(cfg(feature = "yew023")))]
217pub mod yew023;
218
219#[cfg(any(feature = "yew022", feature = "yew023"))]
220mod implicit_clone06;
221
222#[cfg(feature = "ws")]
223#[cfg_attr(doc_cfg, doc(cfg(feature = "ws")))]
224pub mod ws;
225
226#[doc(hidden)]
227pub mod __macros {
228 pub use core::fmt;
229}