Crate async_pub_sub

Source
Expand description

Asynchronous publish-subscribe library for Rust.

§Async Pub Sub

A library that aims at making the pub/sub pattern easy to use in asynchronous Rust.

§Overview

This crate provides a flexible and efficient foundation for building publish-subscribe systems in Rust using asynchronous programming. It includes:

  • Core Abstractions: Publisher, Subscriber traits for defining publishers and subscribers.
  • Derive Macros: Convenient macros available using the macros features to automatically generate publisher and subscriber implementations (see async_pub_sub_macros for more details).
  • Extensibility: Middleware layers for publishers and subscribers to add custom logic like logging or debugging.
  • Example Implementations: Ready-to-use implementations for common use cases.

§Features

  • Asynchronous: Built for async rust.
  • Flexible: Generic implementation allowing to use custom messages.
  • Extensible: Easily add custom middleware layers.
  • Macro Support: Simplify implementation with derive macros.

§Getting Started

Add async_pub_sub to your Cargo.toml:

[dependencies]
async_pub_sub = { version = "0.1.0", features = ["macros"] } # Replace with the latest version

§Example

use async_pub_sub::{
    Publisher, PublisherImpl, Subscriber, SubscriberImpl,
    macros::{DerivePublisher, DeriveSubscriber, routes, rpc_interface},
};

// subscriber wrapper example
#[derive(DeriveSubscriber)]
struct MySubscriber<S>
where
    S: Subscriber,
{
    inner_subscriber: S,
}

// publisher wrapper example
#[derive(DerivePublisher)]
struct MyPublisher<P>
where
    P: Publisher,
{
    inner_publisher: P,
}

// rpc_interface example
// This macro generates elements allowing to perform RPC calls
// between the publisher and subscriber.
#[rpc_interface]
trait MyServiceInterface {
    async fn foo(&self) -> String;
    async fn bar(&mut self, value: i32) -> Result<(), String>;
}

#[derive(DeriveSubscriber)]
struct MyService {
    // `MyServiceInterfaceMessage` is
    // generated by the `rpc_interface` macro
    #[subscriber(MyServiceInterfaceMessage)]
    subscriber: SubscriberImpl<MyServiceInterfaceMessage>,
    value: i32,
}

impl MyServiceInterface for MyService {
    async fn foo(&self) -> String {
        String::from("Hello from MyService!")
    }

    async fn bar(&mut self, value: i32) -> Result<(), String> {
        println!("Received value: {}", value);
        self.value = value;
        Ok(())
    }
}

#[tokio::main]
async fn main() {
    let mut publisher = MyPublisher {
        inner_publisher: PublisherImpl::new("publisher", 1),
    };
    let mut subscriber = MySubscriber {
        inner_subscriber: SubscriberImpl::new("subscriber"),
    };

    let mut my_service = MyService {
        subscriber: SubscriberImpl::new("my_service"),
        value: 0,
    };

    // The `MyServiceInterfaceClient` is generated by the `rpc_interface` macro
    // and allows to perform RPC calls from the publisher to the subscriber.
    let mut client = MyServiceInterfaceClient::new(PublisherImpl::new("client", 1));

    routes!(
        publisher -> subscriber: i32,
        client -> my_service: MyServiceInterfaceMessage,
    )
    .unwrap();

    let publisher_task = tokio::spawn(async move {
        publisher.publish(42).await.unwrap();
    });

    let subscriber_task = tokio::spawn(async move {
        let message = subscriber.receive().await;
        println!("message: {}", message)
    });

    let client_task = tokio::spawn(async move {
        let response = client.foo().await;
        println!("response to foo(): {}", response);

        let response = client.bar(100).await;
        println!("response to bar(): {:?}", response);
    });

    tokio::spawn(async move {
        // The run method is generated automatically for my_service as it implements
        // the `MyServiceInterface` trait and is a subscriber of the `MyServiceInterfaceMessage`.
        // It will handle incoming messages and perform the corresponding RPC calls.
        // The run method is blocking, so it should be run in a separate task.
        my_service.run().await;
    });

    client_task.await.unwrap();
    publisher_task.await.unwrap();
    subscriber_task.await.unwrap();
}

For more examples see the examples/ directory for usage examples.

§License

This project is licensed under the MIT License - see the LICENSE file for details.

§Note

This project is inspired by the Tower project

Re-exports§

pub use futures;
pub use async_pub_sub_macros as macros;

Structs§

DebuggingForwarderLayer
A middleware layer that adds debug logging capabilities to a publisher. When messages are published, it will log them using the debug format.
DebuggingPublisherLayer
A middleware layer that adds debug logging capabilities to a publisher. When messages are published, it will log them using the debug format.
DebuggingSubscriberLayer
A subscriber middleware layer that adds debug logging capabilities. This layer will log all messages using debug format when they are received.
ForwarderBuilder
A builder pattern implementation for constructing a forwarder with middleware layers. This struct allows for composing multiple middleware layers.
ForwarderImpl
A middleware component that logs messages as they pass between a publisher and subscriber.
LoggingPublisherLayer
A publisher middleware layer that adds logging capabilities to any publisher. This layer will log all messages that are published through the publisher.
LoggingSubscriberLayer
A subscriber middleware layer that adds logging capabilities. This layer will log all messages that are received by the subscriber.
PublisherBuilder
A builder pattern implementation for constructing a publisher with middleware layers. This struct allows for composing multiple middleware layers.
PublisherImpl
A concrete implementation of the Publisher trait that handles message distribution to a single subscriber.
Request
A request structure that represents a request-response pattern for asynchronous communication.
SubscriberBuilder
A builder pattern implementation for constructing a subscriber with middleware layers. This struct allows for composing multiple middleware layers.
SubscriberImpl
A concrete implementation of the Subscriber trait that can receive messages from multiple publishers.

Traits§

Forwarder
A trait for types that can both publish and subscribe to messages.
Layer
A trait for creating middleware layers. This trait enables the creation of middleware that can wrap and extend the functionality of other components.
Publisher
A trait for types that can publish messages to subscribers.
PublisherWrapper
A trait for types that wrap a publisher.
Subscriber
Defines the core functionality for a subscriber in the pub/sub system.
SubscriberWrapper
A wrapper trait that provides a unified interface for working with Subscriber implementations.

Type Aliases§

Error
Result