1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
//! # Services: implementation and lifecycle
//!
//! ## Implementing [`crate::Service`]
//!
//! - Define your actor `struct` and implement [`crate::Service`]. Choose `type Stream = …`
//! matching how you construct [`crate::Context`]: use [`crate::EmptyStream`] with
//! [`crate::Context::new`], or a custom stream with [`crate::Context::with_stream`].
//! - Use [`async_trait::async_trait`] on the impl if you override async hooks.
//!
//! ```rust,no_run
//! use async_trait::async_trait;
//! use serviceless::{Context, EmptyStream, Service};
//!
//! #[derive(Default)]
//! struct Worker;
//!
//! #[async_trait]
//! impl Service for Worker {
//! type Stream = EmptyStream<Self>;
//!
//! async fn started(&mut self, _ctx: &mut Context<Self, Self::Stream>) {
//! // runs once before the mailbox loop
//! }
//!
//! async fn stopped(&mut self, _ctx: &mut Context<Self, Self::Stream>) {
//! // runs after the mailbox closes and pending envelopes are drained
//! }
//! }
//! ```
//!
//! ## Starting a service
//!
//! Typical pattern:
//!
//! 1. `let ctx = Context::new();` (or `Context::with_stream(your_stream)`).
//! 2. `let (addr, run) = my_service.start_by_context(ctx);` (equivalently [`crate::Context::run`]).
//! 3. Spawn `run` on your runtime, e.g. `tokio::spawn(run);`.
//!
//! ```rust,no_run
//! use async_trait::async_trait;
//! use serviceless::{Context, EmptyStream, Service};
//!
//! #[derive(Default)]
//! struct Worker;
//!
//! #[async_trait]
//! impl Service for Worker {
//! type Stream = EmptyStream<Self>;
//! }
//!
//! #[tokio::main]
//! async fn main() {
//! let worker = Worker::default();
//! let ctx = Context::new();
//! let (addr, run) = worker.start_by_context(ctx);
//! tokio::spawn(run);
//! // `addr` is usable immediately, but work is only processed after `run` is polled.
//! let _ = addr;
//! }
//! ```
//!
//! Until the run future is polled, **no** mailbox processing happens; queued work from `addr`
//! will not execute.
//!
//! ## Stopping
//!
//! - From inside the actor: [`crate::Context::stop`] closes the mailbox sender; when the
//! channel drains, the run loop ends and [`crate::Service::stopped`] runs.
//! - From outside: [`crate::ServiceAddress::close_service`] has the same effect.
//!
//! ```rust,no_run
//! use async_trait::async_trait;
//! use serviceless::{Context, EmptyStream, Handler, Message, Service};
//!
//! #[derive(Default)]
//! struct Worker;
//!
//! #[async_trait]
//! impl Service for Worker {
//! type Stream = EmptyStream<Self>;
//! }
//!
//! struct Stop;
//! impl Message for Stop {
//! type Result = ();
//! }
//!
//! #[async_trait]
//! impl Handler<Stop> for Worker {
//! async fn handle(
//! &mut self,
//! _: Stop,
//! ctx: &mut Context<Self, Self::Stream>,
//! ) {
//! ctx.stop();
//! }
//! }
//! ```
//!
//! ## Caveats
//!
//! - **[`crate::Context::addr`] before start** is allowed, but if you send before the run future
//! is spawned and advancing, **messages can be lost** or the service may not yet be listening.
//! - **Runtime:** starting or driving the mailbox **outside** a live async runtime is unsupported
//! and can panic; see [`crate::docs::runtime`].