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 fn | Call fn with timeout | Cast fn (non-blocking) |
---|---|---|
call_first_server | call_first_server_with_timeout | cast_first_server |
call_second_server | call_second_server_with_timeout | cast_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 {
// ...
}