musli_web_macros/
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-macros.svg?style=for-the-badge&color=fc8d62&logo=rust" height="20">](https://crates.io/crates/musli-web-macros)
3//! [<img alt="docs.rs" src="https://img.shields.io/badge/docs.rs-musli--web--macros-66c2a5?style=for-the-badge&logoColor=white&logo=data:image/svg+xml;base64,PHN2ZyByb2xlPSJpbWciIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgdmlld0JveD0iMCAwIDUxMiA1MTIiPjxwYXRoIGZpbGw9IiNmNWY1ZjUiIGQ9Ik00ODguNiAyNTAuMkwzOTIgMjE0VjEwNS41YzAtMTUtOS4zLTI4LjQtMjMuNC0zMy43bC0xMDAtMzcuNWMtOC4xLTMuMS0xNy4xLTMuMS0yNS4zIDBsLTEwMCAzNy41Yy0xNC4xIDUuMy0yMy40IDE4LjctMjMuNCAzMy43VjIxNGwtOTYuNiAzNi4yQzkuMyAyNTUuNSAwIDI2OC45IDAgMjgzLjlWMzk0YzAgMTMuNiA3LjcgMjYuMSAxOS45IDMyLjJsMTAwIDUwYzEwLjEgNS4xIDIyLjEgNS4xIDMyLjIgMGwxMDMuOS01MiAxMDMuOSA1MmMxMC4xIDUuMSAyMi4xIDUuMSAzMi4yIDBsMTAwLTUwYzEyLjItNi4xIDE5LjktMTguNiAxOS45LTMyLjJWMjgzLjljMC0xNS05LjMtMjguNC0yMy40LTMzLjd6TTM1OCAyMTQuOGwtODUgMzEuOXYtNjguMmw4NS0zN3Y3My4zek0xNTQgMTA0LjFsMTAyLTM4LjIgMTAyIDM4LjJ2LjZsLTEwMiA0MS40LTEwMi00MS40di0uNnptODQgMjkxLjFsLTg1IDQyLjV2LTc5LjFsODUtMzguOHY3NS40em0wLTExMmwtMTAyIDQxLjQtMTAyLTQxLjR2LS42bDEwMi0zOC4yIDEwMiAzOC4ydi42em0yNDAgMTEybC04NSA0Mi41di03OS4xbDg1LTM4Ljh2NzUuNHptMC0xMTJsLTEwMiA0MS40LTEwMi00MS40di0uNmwxMDItMzguMiAxMDIgMzguMnYuNnoiPjwvcGF0aD48L3N2Zz4K" height="20">](https://docs.rs/musli-web-macros)
4//!
5//! This crate provides the macros used in [Müsli web].
6//!
7//! Please refer to <https://docs.rs/musli> for documentation.
8//!
9//! [Müsli web]: <https://docs.rs/musli-web>
10
11use proc_macro::TokenStream;
12
13mod define;
14
15/// Define API types.
16///
17/// Defining an `endpoint` causes a type to be generated which is a marker type
18/// for the endpoint, which binds together the request and response types.
19///
20/// Defining a broadcast simply associated a broadcast with a marker type.
21///
22/// The marker type is used with the various types used when interacting with an
23/// API endpoint or broadcast, such as:
24///
25/// * [`web::Request`]
26/// * [`web::Listener`]
27///
28/// These are in turn extended with the relevant API using them:
29///
30/// * [`yew021`] for yew `0.21.x`.
31///
32/// <br>
33///
34/// # Macro usage
35///
36/// The macro defines a set of endpoints and broadcasts, each of which is
37/// represented by an uninhabitable type-level marker declared through a `type`
38/// declaration.
39///
40/// On top of the API types, this macro also generates a `debug_id` function
41/// with the following signature:
42///
43/// ```rust
44/// use musli_web::api::MessageId;
45///
46/// fn debug_id(id: MessageId) -> impl core::fmt::Debug {
47///     # "fake"
48/// }
49/// ```
50///
51/// This method can be used to debug a message id, unknown message ids will be
52/// identified with an `Unknown(<number>)` debug printing.
53///
54/// Each type-level marker will implement either [`api::Endpoint`] or
55/// [`api::Broadcast`]. And they will have an associated constant named `ID`
56/// which matches the kind that are assocaited with them.
57///
58/// These roughly follow the structure of:
59///
60/// ```text
61/// pub type <name>;
62///
63/// impl Endpoint for <name> {
64///     <definition>
65/// }
66///
67/// impl Broadcast for <name> {
68///     <definition>
69/// }
70/// ```
71///
72/// Implementing an `Endpoint` can define requests and responses. The first
73/// response defined is required and is the default response that certain APIs
74/// will expected the endpoint to return. Any number of requests can be
75/// specified, this is allows for different "sender types" to be defined, but
76/// their over the wire format has to be the same.
77///
78/// Types specified as request types have to implement [`musli::Encode`] and
79/// types sets as response types must implemente [`musli::Decode`].
80///
81/// ```text
82/// (#[musli(..)])?
83/// pub type Hello;
84///
85/// impl Endpoint for Hello (where <bounds>)? {
86///     impl<'de> Request for HelloRequest<'de> (where <bounds>)?;
87///     type Response<'de> = HelloResponse<'de> (where <bounds>)?;
88/// }
89/// ```
90///
91/// Implementing a `Broadcast` can define events, which are messages sent from
92/// the server to the client. At least one event type is required, which will be
93/// used as the default. Any number of events can be specified which allows for
94/// different "sender types" to be defined, but their over the wire format has
95/// to be the same.
96///
97/// ```text
98/// (#[musli(..)])?
99/// pub type Tick;
100///
101/// impl Broadcast for Tick (where <bounds>)? {
102///     impl<'de> Event TickEvent<'de> (where <bounds>)?;
103///     impl Event for OwnedTickEvent (where <bounds>)?;
104/// }
105/// ```
106///
107/// <br>
108///
109/// # Attributes
110///
111/// * `#[musli(kind = "...")]` - Explicitly sets the kind of an endpoint or
112///   broadcast. Without it a string variant of the name of it will be used.
113///
114/// ```text
115/// #[musli(kind = "tock")]
116/// pub type Tick;
117///
118/// impl Broadcast for Tick {
119///     impl<'de> Event for TickEvent<'de>;
120///     impl Event for OwnedTickEvent;
121/// }
122/// ```
123///
124/// <br>
125///
126/// # Examples
127///
128/// ```
129/// use musli::{Decode, Encode};
130/// use musli_web::api;
131///
132/// #[derive(Encode, Decode)]
133/// pub struct HelloRequest<'de> {
134///     pub message: &'de str,
135/// }
136///
137/// #[derive(Encode, Decode)]
138/// pub struct HelloResponse<'de> {
139///     pub message: &'de str,
140/// }
141///
142/// #[derive(Encode, Decode)]
143/// pub struct TickEvent<'de> {
144///     pub message: &'de str,
145///     pub tick: u32,
146/// }
147///
148/// #[derive(Encode, Decode)]
149/// pub struct OwnedTickEvent {
150///     pub message: String,
151///     pub tick: u32,
152/// }
153///
154/// api::define! {
155///     pub type Hello;
156///
157///     impl Endpoint for Hello {
158///         impl<'de> Request for HelloRequest<'de>;
159///         type Response<'de> = HelloResponse<'de>;
160///     }
161///
162///     #[musli(id = 100)]
163///     pub type Tick;
164///
165///     impl Broadcast for Tick {
166///         impl<'de> Event for TickEvent<'de>;
167///         impl Event for OwnedTickEvent;
168///     }
169/// }
170///
171/// assert_eq!(format!("{:?}", debug_id(Hello::ID)), "Hello");
172/// assert_eq!(format!("{:?}", debug_id(Tick::ID)), "Tick");
173/// ```
174///
175/// [`api::Broadcast`]: <https://docs.rs/musli-web/latest/musli_web/api/trait.Broadcast.html>
176/// [`api::Endpoint`]: <https://docs.rs/musli-web/latest/musli_web/api/trait.Endpoint.html>
177/// [`musli::Decode`]: <https://docs.rs/musli/latest/musli/trait.Decode.html>
178/// [`musli::Encode`]: <https://docs.rs/musli/latest/musli/trait.Encode.html>
179/// [`web::Listener`]: <https://docs.rs/musli-web/latest/musli_web/web/struct.Listener.html>
180/// [`web::Request`]: <https://docs.rs/musli-web/latest/musli_web/web/struct.Request.html>
181/// [`yew021`]: <https://docs.rs/musli-web/latest/musli_web/yew021/>
182#[proc_macro]
183pub fn define(input: TokenStream) -> TokenStream {
184    let path = syn::parse_quote!(::musli_web);
185    let cx = define::cx(&path);
186
187    let stream = define::expand(&cx, input.into());
188
189    if let Some(stream) = cx.into_compile_errors() {
190        return stream.into();
191    };
192
193    stream.into()
194}