Attribute Macro genserver::make_registry

source · []
#[make_registry]
Expand description

Makes a registry.

This attribute applies to structs and converts an ordinary struct into a registry. While you can pass any ordinary struct to this attribute, it’s recommended that you don’t add any fields or methods to the registry crate.

You must specify the servers you want to register as an argument, in name: Type pairs.

For example, if we use the following code:

#[make_registry {
    first_server: FirstServer,
    second_server: SecondServer,
}]
struct MyRegistry;

This will generate a registry called MyRegistry which implements the Registry trait. In the example above, we specified 2 servers, named first_server and second_server, and are of the type FirstServer and SecondServer respectively.

For each server registered with this registry, the call_{name}(), call_{name}_with_timeout(), and cast_{name}() will be generated for this registry which can be used to make calls and casts to each server. You can (if you wish) define multiple separate instances of the same type, so long as each one has a unique name, much like you would when defining struct fields.

Using the example above, we’d generate functions with the following names in our registry:

Call fnCall fn with timeoutCast fn (non-blocking)
call_first_servercall_first_server_with_timeoutcast_first_server
call_second_servercall_second_server_with_timeoutcast_second_server

This macro will additionally derive the Clone trait.

Note that this example will not compile because the GenServer trait is not implemented for this example case.

The full generated code for the example above is as follows:

struct MyRegistry {
    first_server_tx: tokio::sync::mpsc::Sender<(
        <FirstServer as genserver::GenServer>::Message,
        Option<tokio::sync::oneshot::Sender<<FirstServer as genserver::GenServer>::Response>>,
    )>,
    second_server_tx: tokio::sync::mpsc::Sender<(
        <SecondServer as genserver::GenServer>::Message,
        Option<tokio::sync::oneshot::Sender<<SecondServer as genserver::GenServer>::Response>>,
    )>,
}
#[automatically_derived]
impl ::core::clone::Clone for MyRegistry {
    #[inline]
    fn clone(&self) -> MyRegistry {
        MyRegistry {
            first_server_tx: ::core::clone::Clone::clone(&self.first_server_tx),
            second_server_tx: ::core::clone::Clone::clone(&self.second_server_tx),
        }
    }
}
impl genserver::Registry for MyRegistry {}
impl MyRegistry {
    pub async fn start() -> Self {
        let (first_server_tx, mut first_server_rx) = tokio::sync::mpsc::channel::<(
            <FirstServer as genserver::GenServer>::Message,
            Option<
                tokio::sync::oneshot::Sender<<FirstServer as genserver::GenServer>::Response>,
            >,
        )>(1_000);
        let (second_server_tx, mut second_server_rx) = tokio::sync::mpsc::channel::<(
            <SecondServer as genserver::GenServer>::Message,
            Option<
                tokio::sync::oneshot::Sender<<SecondServer as genserver::GenServer>::Response>,
            >,
        )>(1_000);
        let mut registry = Self {
            first_server_tx,
            second_server_tx,
        };
        {
            let local_registry = registry.clone();
            tokio::spawn(async move {
                let mut handler = FirstServer::new(local_registry);
                while let Some((message, oneshot)) = first_server_rx.recv().await {
                    if let Some(oneshot) = oneshot {
                        let Response = handler.handle_call(message).await;
                        oneshot.send(Response).ok();
                    } else {
                        handler.handle_cast(message).await;
                    }
                }
            });
        }
        {
            let local_registry = registry.clone();
            tokio::spawn(async move {
                let mut handler = SecondServer::new(local_registry);
                while let Some((message, oneshot)) = second_server_rx.recv().await {
                    if let Some(oneshot) = oneshot {
                        let Response = handler.handle_call(message).await;
                        oneshot.send(Response).ok();
                    } else {
                        handler.handle_cast(message).await;
                    }
                }
            });
        }
        registry
    }

    pub async fn call_first_server(
        &self,
        message: <FirstServer as genserver::GenServer>::Message,
    ) -> Result<
        <FirstServer as genserver::GenServer>::Response,
        genserver::Error<(
            <FirstServer as genserver::GenServer>::Message,
            Option<
                tokio::sync::oneshot::Sender<<FirstServer as genserver::GenServer>::Message>,
            >,
        )>,
    > {
        let (oneshot_tx, oneshot_rx) =
            tokio::sync::oneshot::channel::<<FirstServer as genserver::GenServer>::Response>();
        self.first_server_tx
            .send((message, Some(oneshot_tx)))
            .await?;
        let Response = oneshot_rx.await?;
        Ok(Response)
    }

    pub async fn cast_first_server(
        &self,
        message: <FirstServer as genserver::GenServer>::Message,
    ) -> Result<
        (),
        genserver::Error<(
            <FirstServer as genserver::GenServer>::Message,
            Option<
                tokio::sync::oneshot::Sender<<FirstServer as genserver::GenServer>::Message>,
            >,
        )>,
    > {
        self.first_server_tx.send((message, None)).await?;
        Ok(())
    }

    pub async fn call_second_server(
        &self,
        message: <SecondServer as genserver::GenServer>::Message,
    ) -> Result<
        <SecondServer as genserver::GenServer>::Response,
        genserver::Error<(
            <SecondServer as genserver::GenServer>::Message,
            Option<
                tokio::sync::oneshot::Sender<<SecondServer as genserver::GenServer>::Message>,
            >,
        )>,
    > {
        let (oneshot_tx, oneshot_rx) =
            tokio::sync::oneshot::channel::<<SecondServer as genserver::GenServer>::Response>();
        self.second_server_tx
            .send((message, Some(oneshot_tx)))
            .await?;
        let Response = oneshot_rx.await?;
        Ok(Response)
    }

    pub async fn cast_second_server(
        &self,
        message: <SecondServer as genserver::GenServer>::Message,
    ) -> Result<
        (),
        genserver::Error<(
            <SecondServer as genserver::GenServer>::Message,
            Option<
                tokio::sync::oneshot::Sender<<SecondServer as genserver::GenServer>::Message>,
            >,
        )>,
    > {
        self.second_server_tx.send((message, None)).await?;
        Ok(())
    }
}

Adding your own state

If you’d like to add your own state to a registry, you can do so like you normally would with any other struct, so long as the fields are named (you can’t use an unnamed field struct). Any types you add must also implement Send, Sync, and Clone.

When you specify fields in a struct, it will create a new() method which takes all those fields by value for initialization. For example, this code:

use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::Arc;

use genserver::make_registry;

#[make_registry{}]
struct MyRegistry {
    counter: Arc<AtomicUsize>,
}

Will generate a registry with a new() which has the following signature:

pub async fn start(counter: Arc<AtomicUsize>) -> Self {
    // ...
}