define

Macro define 

Source
define!() { /* proc-macro */ }
Available on crate feature api only.
Expand description

Define API types.

Defining an endpoint causes a type to be generated which is a marker type for the endpoint, which binds together the request and response types.

Defining a broadcast simply associated a broadcast with a marker type.

The marker type is used with the various types used when interacting with an API endpoint or broadcast, such as:

These are in turn extended with the relevant API using them:


§Macro usage

The macro defines a set of endpoints and broadcasts, each of which is represented by an uninhabitable type-level marker declared through a type declaration.

On top of the API types, this macro also generates a debug_id function with the following signature:

use musli_web::api::MessageId;

fn debug_id(id: MessageId) -> impl core::fmt::Debug {
}

This method can be used to debug a message id, unknown message ids will be identified with an Unknown(<number>) debug printing.

Each type-level marker will implement either api::Endpoint or api::Broadcast. And they will have an associated constant named ID which matches the kind that are assocaited with them.

These roughly follow the structure of:

pub type <name>;

impl Endpoint for <name> {
    <definition>
}

impl Broadcast for <name> {
    <definition>
}

Implementing an Endpoint can define requests and responses. The first response defined is required and is the default response that certain APIs will expected the endpoint to return. Any number of requests can be specified, this is allows for different “sender types” to be defined, but their over the wire format has to be the same.

Types specified as request types have to implement musli::Encode and types sets as response types must implemente musli::Decode.

(#[musli(..)])?
pub type Hello;

impl Endpoint for Hello (where <bounds>)? {
    impl<'de> Request for HelloRequest<'de> (where <bounds>)?;
    type Response<'de> = HelloResponse<'de> (where <bounds>)?;
}

Implementing a Broadcast can define events, which are messages sent from the server to the client. At least one event type is required, which will be used as the default. Any number of events can be specified which allows for different “sender types” to be defined, but their over the wire format has to be the same.

(#[musli(..)])?
pub type Tick;

impl Broadcast for Tick (where <bounds>)? {
    impl Event (<'lt>)? for OwnedTickEvent (where <bounds>)?;
}

§Attributes

  • #[musli(kind = "...")] - Explicitly sets the kind of an endpoint or broadcast. Without it a string variant of the name of it will be used.
#[musli(kind = "tock")]
pub type Tick;

impl Broadcast for Tick {
    impl Event for OwnedTickEvent;
    impl<'de> Event for TickEvent<'de>;
}

§Examples

use musli::{Decode, Encode};
use musli_web::api;

#[derive(Encode, Decode)]
pub struct HelloRequest<'de> {
    pub message: &'de str,
}

#[derive(Encode, Decode)]
pub struct HelloResponse<'de> {
    pub message: &'de str,
}

#[derive(Encode, Decode)]
pub struct TickEvent<'de> {
    pub message: &'de str,
    pub tick: u32,
}

#[derive(Encode, Decode)]
pub struct OwnedTickEvent {
    pub message: String,
    pub tick: u32,
}

api::define! {
    pub type Hello;

    impl Endpoint for Hello {
        impl<'de> Request for HelloRequest<'de>;
        type Response<'de> = HelloResponse<'de>;
    }

    #[musli(id = 100)]
    pub type Tick;

    impl Broadcast for Tick {
        impl Event for OwnedTickEvent;
        impl<'de> Event for TickEvent<'de>;
    }
}

assert_eq!(format!("{:?}", debug_id(Hello::ID)), "Hello");
assert_eq!(format!("{:?}", debug_id(Tick::ID)), "Tick");