Crate lifeline[][src]

Lifeline is a dependency injection library for message-based applications. Lifeline produces applications which are:

  • Clean: Bus implementations provide a high-level overview of the application, and services clearly define the messages they send and receive.
  • Decoupled: Services and tasks have no dependency on their peers, as they only depend on the message types they are sending or receiving.
  • Stoppable: Services and tasks are trivially cancellable. For example, you can terminate all tasks associated with a connection when a client disconnects.
  • Greppable: The impact/reach of a message can be easily understood by searching for the type in the source code.
  • Testable: Lifeline applications communicate via messages, which makes unit testing easy. Spawn the service, send a message, and expect a received message.

In order to achieve these goals, lifeline provides patterns, traits, and implementations:

  • The Bus, which constructs & distributes channel Senders/Receivers, and Resources.
  • The Carrier, which translates messages between two Bus instances. Carriers are critical when building large applications, and help minimize the complexity of the messages on each bus.
  • The Service, which takes channels from the bus, and spawns tasks which send and receive messages.
  • The Task, an async future which returns a lifeline when spawned. When the lifeline is dropped, the future is immedately cancelled.
  • The Resource, a struct which can be stored in the bus, and taken (or cloned) when services spawn.

For a quick introduction, see the hello.rs example. For a full-scale application see tab-rs.

Quickstart

Lifeline can be used with the tokio and async-std runtimes. By default, lifeline uses tokio.

lifeline = "0.6"

async-std can be enabled with the async-std-executor feature. And the mpsc implementation can be enabled with the async-std-channels feature:

lifeline = { version = "0.6", default-features = false, features = ["dyn-bus", "async-std-executor", "async-std-channels"] }

Lifeline also supports postage channels, a library that provides a portable set of channel implementations (compatible with any executor). Postage also provides Stream and Sink combinators (similar to futures StreamExt), that are optimized for async channels.
Postage is intended to replace the LifelineSender/LifelineReceiver wrappers that were removed in lifeline v0.6.0.

Upgrading

v0.6.0 contains several breaking changes:

  • The LifelineSender and LifelineReceiver wrappers were removed. This was necessary due to the recent changes in the Stream ecosystem, and the upcoming stabilization of the Stream RFC. If you need Stream/Sink combinators, take a look at postage, or tokio-stream.
  • The barrier channel was removed. It can be replaced with postage::barrier.
  • The subscription channel was removed. If you need it back, you can find the code before the removal here.
  • The Sender and Receiver traits were removed from prelude. This is so that importing the lifeline prelude does not conflict with Sink/Stream traits. You can import them with: use lifeline::{Sender, Receiver}.

The Bus

The Bus carries channels and resources, and allows you to write loosely coupled Service implementations which communicate over messages.

Channels can be taken from the bus. If the channel endpoint is clonable, it will remain available for other services.
If the channel is not clonable, future calls will receive an Err value. The Rx/Tx type parameters are type-safe, and will produce a compile error if you attempt to take a channel for an message type which the bus does not carry.

Lifeline provides a lifeline_bus! macro which stores channels and resources in Box<dyn> slots:

use lifeline::lifeline_bus;
lifeline_bus!(pub struct MainBus);

The Carrier

Carriers provide a way to move messages between busses. Carriers can translate, ignore, or collect information, providing each bus with the messages that it needs.

Large applications have a tree of Busses. This is good, it breaks your app into small chunks.

- MainBus
  | ConnectionListenerBus
  |  | ConnectionBus
  | DomainSpecificBus
  |  | ...

Carriers allow each bus to define messages that minimally represent the information it's services need to function, and prevent an explosion of messages which are copied to all busses.

Carriers centralize the communication between busses, making large applications easier to reason about.

The Service

The Service synchronously takes channels from the Bus, and spawns a tree of async tasks (which send & receive messages). When spawned, the service returns one or more Lifeline values. When a Lifeline is dropped, the associated task is immediately cancelled.

It's common for Service::spawn to return a Result. Taking channel endpoints is a fallible operation. Depending on the channel type, the endpoint may not be clonable. Lifeline clones endpoints when it can (e.g. for mpsc::Sender, broadcast::*, and watch::Receiver). Other endpoints are taken, removed, and future calls will return an Err.

Service::spawn takes channels from the bus synchronously, which makes errors occur predictably and early. If you get an Err on an mpsc::Receiver, change it's binding in the bus to broadcast::Sender.

The Task

The Task executes an Future, and returns a Lifeline when spawned. When the lifeline is dropped, the future is immediately cancelled.

Task trait is implemented for all types - you can import it and use Self::task in any type. In lifeline, it's most commonly used in Service implementations.

The Resource

Resources can be stored on the bus. This is very useful for configuration (e.g MainConfig), or connections (e.g. a TcpStream).

Resources implement the Storage trait, which is easy with the impl_storage_clone! and impl_storage_take! macros.

Modules

dyn_bus

The DynBus implementation used by lifeline_bus!, and TypeId-based slot storage.

error

All the lifeline error types.

prelude

Prelude, including all the traits and types required for typical lifeline usage.

request

A request/response helper type, which can be sent over messages.

test

Helpers which assist in testing applications based on lifeline.

Macros

assert_completes

Asserts that the expression completes within a given number of milliseconds.

assert_times_out

Asserts that the expression does not complete within a given number of milliseconds.

impl_channel_clone

Specifies that this channel endpoint (Sender or Receiver) is clonable. Provides a generic type T with the bounds required for the implementation.

impl_channel_take

Specifies that this channel endpoint (Sender or Receiver) is taken. Provides a generic type T with the bounds required for the implementation.

impl_storage_clone

Specifies that this resource is cloned.

impl_storage_take

Specifies that this resource is taken, and is !Clone.

lifeline_bus

Defines a lifeline bus: it's struct, Bus impl, and DynBus impl.

Structs

Lifeline

A lifeline value, associated with a future spawned via the Task trait. When the lifeline is dropped, the associated future is immediately cancelled.

Enums

Link

Represents the Sender, Receiver, or Both. Used in error types.

Traits

Bus

Stores and distributes channel endpoints (Senders and Receivers), as well as Resource values.

CarryFrom

Carries messages between two bus instances. A variant of the Service.

CarryInto

The receprocial of the CarryFrom trait. Implemented for all types on which CarryFrom is implemented.

Channel

A channel's (Sender, Receiver) pair. Defines how the bus constructs and retrieves the values.

DefaultCarrier

Constructs two bus types, and spawns the carrier between them. Returns both busses, and the carrier's lifeline.

DefaultService

Constructs the bus, spawns the service, and returns both.

Message

Attaches a channel to the Bus, carrying Self as a message.

Receiver

The receiver half of an asynchronous channel, which may be bounded/unbounded, mpsc/broadcast/oneshot, etc.

Resource

Attaches a resource to the Bus. This resource can accessed from the bus using bus.resource::<Self>().

Sender

The sender half of an asynchronous channel, which may be bounded/unbounded, mpsc/broadcast/oneshot, etc.

Service

Takes channels from the Bus, and spawns a tree of tasks. Returns one or more Lifeline values.
When the Lifeline is dropped, the task tree is immediately cancelled.

Storage

Defines a resource (or channel endpoint) which can be stored on the bus, and how it is taken or cloned.

Task

Provides the Self::task and Self::try_task associated methods for all types.