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 init(&mut self) -> LocalBoxFuture<'_, Result<(), String>> {
76        Box::pin(async { Ok(()) })
77    }
78    fn run_task(&mut self) -> LocalBoxFuture<'_, Result<(), String>>;
79}
80
81/// Any type that will be sent between services needs to implement this trait.
82///
83/// Sending a Reply is optional (use `type Reply = ()` if you will not send a reply).
84pub trait Message: Send + Sync + Any + 'static {
85    type Reply: Send;
86}
87
88/// Implement this trait to indicate that your service can process a specific message.
89///
90/// The `deliver()` method is passed a `&mut self` reference to the service,
91/// making it very easy to update your state. You can optionally include a
92/// reply.
93pub trait Handler<M: Message> {
94    fn deliver(&mut self, m: M) -> M::Reply;
95}
96
97/// Blanket implementation of Message for any Vec<M>. You lose the return value.
98impl<M: Message> Message for Vec<M> {
99    type Reply = ();
100}
101
102/// Blanket implementation of delivering a `Vec<Message>` to a `Handler<Message>`.
103impl<M: Message, S: Handler<M>> Handler<Vec<M>> for S {
104    fn deliver(&mut self, messages: Vec<M>) {
105        for m in messages {
106            self.deliver(m);
107        }
108    }
109}