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 Event (<'lt>)? for OwnedTickEvent (where <bounds>)?;
103/// }
104/// ```
105///
106/// <br>
107///
108/// # Attributes
109///
110/// * `#[musli(kind = "...")]` - Explicitly sets the kind of an endpoint or
111/// broadcast. Without it a string variant of the name of it will be used.
112///
113/// ```text
114/// #[musli(kind = "tock")]
115/// pub type Tick;
116///
117/// impl Broadcast for Tick {
118/// impl Event for OwnedTickEvent;
119/// impl<'de> Event for TickEvent<'de>;
120/// }
121/// ```
122///
123/// <br>
124///
125/// # Examples
126///
127/// ```
128/// use musli::{Decode, Encode};
129/// use musli_web::api;
130///
131/// #[derive(Encode, Decode)]
132/// pub struct HelloRequest<'de> {
133/// pub message: &'de str,
134/// }
135///
136/// #[derive(Encode, Decode)]
137/// pub struct HelloResponse<'de> {
138/// pub message: &'de str,
139/// }
140///
141/// #[derive(Encode, Decode)]
142/// pub struct TickEvent<'de> {
143/// pub message: &'de str,
144/// pub tick: u32,
145/// }
146///
147/// #[derive(Encode, Decode)]
148/// pub struct OwnedTickEvent {
149/// pub message: String,
150/// pub tick: u32,
151/// }
152///
153/// api::define! {
154/// pub type Hello;
155///
156/// impl Endpoint for Hello {
157/// impl<'de> Request for HelloRequest<'de>;
158/// type Response<'de> = HelloResponse<'de>;
159/// }
160///
161/// #[musli(id = 100)]
162/// pub type Tick;
163///
164/// impl Broadcast for Tick {
165/// impl Event for OwnedTickEvent;
166/// impl<'de> Event for TickEvent<'de>;
167/// }
168/// }
169///
170/// assert_eq!(format!("{:?}", debug_id(Hello::ID)), "Hello");
171/// assert_eq!(format!("{:?}", debug_id(Tick::ID)), "Tick");
172/// ```
173///
174/// [`api::Broadcast`]: <https://docs.rs/musli-web/latest/musli_web/api/trait.Broadcast.html>
175/// [`api::Endpoint`]: <https://docs.rs/musli-web/latest/musli_web/api/trait.Endpoint.html>
176/// [`musli::Decode`]: <https://docs.rs/musli/latest/musli/trait.Decode.html>
177/// [`musli::Encode`]: <https://docs.rs/musli/latest/musli/trait.Encode.html>
178/// [`web::Listener`]: <https://docs.rs/musli-web/latest/musli_web/web/struct.Listener.html>
179/// [`web::Request`]: <https://docs.rs/musli-web/latest/musli_web/web/struct.Request.html>
180/// [`yew021`]: <https://docs.rs/musli-web/latest/musli_web/yew021/>
181#[proc_macro]
182pub fn define(input: TokenStream) -> TokenStream {
183 let path = syn::parse_quote!(::musli_web);
184 let cx = define::cx(&path);
185
186 let stream = define::expand(&cx, input.into());
187
188 if let Some(stream) = cx.into_compile_errors() {
189 return stream.into();
190 };
191
192 stream.into()
193}