memfault_ssf/
lib.rs

1//
2// Copyright (c) Memfault, Inc.
3// See License.txt for details
4//! Simple Service Framework
5//!
6//! This library provides a simple abstraction to implement a service oriented
7//! architecture in a non-async Rust program.
8//!
9//! Each service runs in its own thread with a processing loop waiting on a
10//! channel.
11//!
12//! This library provides a few essential traits:
13//!
14//! - `Service`: a trait implemented by services
15//! - `Message`: a trait implemented by messages
16//! - `Handler<M: Message>`: a trait implemented by services that can handle
17//! messages of type `M`.
18//!
19//! We provide some important structs to deploy the services:
20//! - `ServiceThread`: will start and run a service inside a dedicated thread.
21//! It returns a `Mailbox`.
22//! - `Mailbox<S: Service>`: a lightweight (cheap to `clone()`) handle to send
23//! messages to a thread.
24//! - `Scheduler`: a utility thread which keeps a schedule of messages that need
25//! to be sent at fixed intervals.
26//!
27//! As well as important testing utilities that are a big part of the value
28//! provided by this framework:
29//! - `ServiceJig`: a way to run a service inside a test without using threads.
30//! The test can precisely decide when messages should be delivered and inspect
31//! the state of the service at any time.
32//! - `ServiceMock`: a service mock. Use this when you just need a place where
33//! to send messages. Your test can then verify that the right messages were
34//! sent to the mock.
35//!
36
37use std::any::Any;
38
39mod envelope;
40mod mailbox;
41mod msg_mailbox;
42mod scheduler;
43mod service_jig;
44mod service_manager;
45mod service_mock;
46mod service_thread;
47mod shared_service_thread;
48mod stats;
49mod system_messages;
50
51pub use envelope::*;
52use futures::future::LocalBoxFuture;
53pub use mailbox::*;
54pub use msg_mailbox::*;
55pub use scheduler::*;
56pub use service_jig::*;
57pub use service_manager::*;
58pub use service_mock::*;
59pub use service_thread::*;
60pub use shared_service_thread::*;
61pub use stats::*;
62pub use system_messages::*;
63
64/// All services should implement this trait. It guarantees that we will be able
65/// to run the service inside a thread (it is `Send`).
66pub trait Service {
67    fn name(&self) -> &str;
68}
69
70/// Implement this trait if your service needs to run a task in the background.
71///
72/// The implementation here does not need to be `Send` as we only run the
73/// task on a single thread executor.
74pub trait TaskService: Service {
75    fn run_task(&mut self) -> LocalBoxFuture<'_, Result<(), String>>;
76}
77
78/// Any type that will be sent between services needs to implement this trait.
79///
80/// Sending a Reply is optional (use `type Reply = ()` if you will not send a reply).
81pub trait Message: Send + Sync + Any + 'static {
82    type Reply: Send;
83}
84
85/// Implement this trait to indicate that your service can process a specific message.
86///
87/// The `deliver()` method is passed a `&mut self` reference to the service,
88/// making it very easy to update your state. You can optionally include a
89/// reply.
90pub trait Handler<M: Message> {
91    fn deliver(&mut self, m: M) -> M::Reply;
92}
93
94/// Blanket implementation of Message for any Vec<M>. You lose the return value.
95impl<M: Message> Message for Vec<M> {
96    type Reply = ();
97}
98
99/// Blanket implementation of delivering a `Vec<Message>` to a `Handler<Message>`.
100impl<M: Message, S: Handler<M>> Handler<Vec<M>> for S {
101    fn deliver(&mut self, messages: Vec<M>) {
102        for m in messages {
103            self.deliver(m);
104        }
105    }
106}