[][src]Crate revent

Synchronous event system.

What is an event system?

An event system is a set of objects that can exchange messages with each other. The objects know nothing about each other, they just know their common contracts. This allows for code to be separated along logical boundaries, thus reducing complexity and increasing testability.

In revent each such object is called a Node, as it represents a node in a graph. Each node decides for itself which channels it wants to listen to, as well as emit to. Using this information we can construct a complete graph of node-to-node interactions. As we'll see, this graph is a proper directed acyclic graph (DAG), and is important for Rust's mutable aliasing rules.

Revent's events are synchronous, meaning that emitting an event will immediately process all handlers of that event. Once the function call returns, it is guaranteed that all listeners have been called.

Extra

As a visual aid, the documentation tests contain a Grapher. To acquire the images you'll need to run these code snippets and have graphviz installed.

Mutable cycles

Revent performs cycle detection in subscribe and ensures that no system exists in which we can create double mutable borrows. After subscription, no cycle detection is performed, so the actual emitting and receiving part has the lowest possible overhead.

Core Concepts

A revent system has 3 core concepts:

  • Channels
  • Anchors
  • Nodes

Channel

A channel is a container for item(s) that listen to that particular channel. Any signal emission on said channel will notify all items in that channel.

In this documentation channel denotes Slot, Single, as well as feed.

Anchor

An anchor contains all channels in a system. It also contains a Manager and implements Anchor. We register new Nodes to an anchor, and these nodes will themselves choose which channels to listen or emit to. Only anchors can be subscribed to.

Nodes

A node implements Node which contains the functions:

These specify the channels to emit and listen to. Any struct can be a node. Nodes are constructed by first constructing the emitter structure as specified by register_emits. This structure is then provided to the Anchor::subscribe create function.

When talking about the nodes subscribed (as per register_listens) to a channel, the term subscriber may be used.

Example

use revent::{Anchor, Grapher, Manager, Node, Slot};
use std::{cell::RefCell, rc::Rc};

// First let's crate a hub (Anchor) that contains two signals.

trait BasicSignal {
    fn basic(&mut self);
}

struct MyAnchor {
    basic_slot_1: Slot<dyn BasicSignal>,
    basic_slot_2: Slot<dyn BasicSignal>,
    mng: Manager,
}
impl MyAnchor {
    fn new() -> Self {
        let mng = Manager::new();
        Self {
            basic_slot_1: Slot::new("basic_slot_1", &mng),
            basic_slot_2: Slot::new("basic_slot_2", &mng),
            mng,
        }
    }
}
impl Anchor for MyAnchor {
    fn manager(&self) -> &Manager {
        &self.mng
    }
}

// ---

// Now we define our emitter structure, this one contains only `basic_slot_2`, which indicates
// that we want to emit only to this slot for the subscribers using it as their register_emits.

struct MyEmitter {
    basic_slot_2: Slot<dyn BasicSignal>,
}

// ---

// Create a node that uses MyEmitter (emits on `basic_slot_2`), and listens on
// `basic_slot_1`.

struct MyNode { emits: MyEmitter }
impl Node<MyAnchor, MyEmitter> for MyNode {
    // Indicate which slots we want to use.
    fn register_emits(hub: &MyAnchor) -> MyEmitter {
        MyEmitter {
            basic_slot_2: hub.basic_slot_2.clone(),
        }
    }

    fn register_listens(hub: &mut MyAnchor, item: Rc<RefCell<Self>>) {
        hub.basic_slot_1.register(item);
    }
    const NAME: &'static str = "MyNode";
}

// Whenever we get a basic signal we pass it to the register_emits.
impl BasicSignal for MyNode {
    fn basic(&mut self) {
        self.emits.basic_slot_2.emit(|_| println!("Hello world"));
    }
}

// ---

let mut hub = MyAnchor::new();
// The type annotation is not needed, but shown here to show what to expect.
let item = hub.subscribe(|emits: MyEmitter| MyNode { emits });
hub.basic_slot_1.emit(BasicSignal::basic);
hub.unsubscribe(&item);

Grapher::new(hub.manager()).graph_to_file("target/example-with-emitter.png").unwrap();

Modules

feed

Cycle-breaking poll-based backward channels.

Structs

Grapher

Wrapper around a Manager that generates a graph.

Manager

Inspects how various Nodes use slots.

Single

Single slot containing T.

Slot

List of Nodes to a signal T.

Traits

Anchor

A collection of channels to which Nodes can subscribe.

Node

Describes a subscriber that can subscribe to Anchor.