genserver 0.1.10

genserver is an Elixir inspired async actor library
Documentation

Docs Crates.io Build & test Codecov

genserver: generate a server

genserver is tiny async actor framework, inspired by Elixir's GenServer. With few lines code, we do great things.

Check out the docs for more details.

Synopsis

First run:

$ cargo add genserver

Then try this code here in your crate (note the 2 features which need to be enabled in main.rs or lib.rs for applications and libraries respectively):

// these two features must be enabled at the crate level
#![feature(generic_associated_types, type_alias_impl_trait)]

use std::future::Future;

use genserver::{make_registry, GenServer};

struct MyServer {
    // Any state your server needs can go right
    // in here. We keep a registry around in case
    // we want to call any other servers from our
    // server.
    registry: MyRegistry,
}

impl GenServer for MyServer {
    // Message type for this server.
    type Message = String;
    // The type of the registry defined by the `make_registry` attribute macro.
    type Registry = MyRegistry;
    // The response type for calls.
    type Response = String;

    // The call response type, a future, which returns Self::Response.
    type CallResponse<'a> = impl Future<Output = Self::Response> + 'a;
    // The cast response type, also a future, which returns unit.
    type CastResponse<'a> = impl Future<Output = ()> + 'a;

    fn new(registry: Self::Registry) -> Self {
        // When are server is started, the registry passes a copy
        // of itself here. We keep it around so we can call
        // other servers from this one.
        Self { registry }
    }

    // Calls to handle_call will block until our `Response` is returned.
    // Because they return a future, we can return an async block here.
    fn handle_call(&mut self, message: Self::Message) -> Self::CallResponse<'_> {
        println!("handle_call received {}", message);
        std::future::ready("returned from handle_call".into())
    }

    // Casts always return (), because they do not block callers and return
    // immediately.
    fn handle_cast(&mut self, message: Self::Message) -> Self::CastResponse<'_> {
        println!("handle_cast received {}", message);
        std::future::ready(())
    }
}

#[make_registry{
    myserver: MyServer
}]
struct MyRegistry;

tokio_test::block_on(async {
    let registry = MyRegistry::start().await;

    let response = registry
        .call_myserver("calling myserver".into())
        .await
        .unwrap();
    registry
        .cast_myserver("casting to myserver".into())
        .await
        .unwrap();
});