atomr_core/actor/traits.rs
1//! Core `Actor` trait and message envelope.
2
3use async_trait::async_trait;
4
5use super::context::Context;
6use super::metadata::Metadata;
7use super::sender::Sender;
8use crate::supervision::{Directive, SupervisorStrategy};
9
10/// Envelope that carries a user message plus a typed [`Sender`].
11///
12/// `M` is the actor's user message type. The [`Sender`] preserves the
13/// origin's identity end-to-end (no `Any::downcast` on reply paths) —
14/// see `docs/idiomatic-rust.md` (P-1) and Phase 1 of
15/// `docs/full-port-plan.md`.
16pub struct MessageEnvelope<M> {
17 pub message: M,
18 pub sender: Sender,
19 /// Trace context + baggage propagated across hops (FR-10). Empty unless a
20 /// sender attached it via [`tell_with_meta`](super::ActorRef::tell_with_meta)
21 /// or an interceptor injected it.
22 pub metadata: Metadata,
23}
24
25impl<M> MessageEnvelope<M> {
26 pub fn new(message: M) -> Self {
27 Self { message, sender: Sender::None, metadata: Metadata::new() }
28 }
29
30 /// Construct with a typed [`Sender`].
31 pub fn with_typed_sender(message: M, sender: Sender) -> Self {
32 Self { message, sender, metadata: Metadata::new() }
33 }
34
35 /// Construct with a typed [`Sender`] and [`Metadata`].
36 pub fn with_meta(message: M, sender: Sender, metadata: Metadata) -> Self {
37 Self { message, sender, metadata }
38 }
39}
40
41/// The user-facing `Actor` trait.
42///
43/// is expressed here as: each actor has an
44/// associated `Msg` type (typically an enum) and implements an async
45/// `handle` that matches on it.
46#[async_trait]
47pub trait Actor: Sized + Send + 'static {
48 type Msg: Send + 'static;
49
50 /// Process a single message.
51 async fn handle(&mut self, ctx: &mut Context<Self>, msg: Self::Msg);
52
53 /// Called once before the first message.
54 async fn pre_start(&mut self, _ctx: &mut Context<Self>) {}
55
56 /// Called after the actor has been stopped.
57 async fn post_stop(&mut self, _ctx: &mut Context<Self>) {}
58
59 /// Called when the actor is about to be restarted by the supervisor.
60 async fn pre_restart(&mut self, _ctx: &mut Context<Self>, _err: &str) {}
61
62 /// Called after a restart.
63 async fn post_restart(&mut self, _ctx: &mut Context<Self>, _err: &str) {}
64
65 /// Called when a watched actor terminates. The `path` argument is
66 /// the path of the actor that just stopped. Default is a no-op.
67 /// Implementations may translate this into a user-visible message
68 /// (the Python binding does this for `Terminated` events).
69 async fn on_terminated(&mut self, _ctx: &mut Context<Self>, _path: &super::path::ActorPath) {}
70
71 /// Called when the supervisor pushes a *graded* operating-mode change
72 /// ([`Directive::Throttle`], [`Directive::Suspend`], or
73 /// [`Directive::ResumeFrom`]) into this still-running actor — no restart,
74 /// state preserved (FR-6). The actor is expected to gate its own outbound
75 /// effects (order rate/size, flat-only, etc.) accordingly. Default no-op.
76 ///
77 /// The crash-recovery directives (`Resume`/`Restart`/`Stop`/`Escalate`) do
78 /// **not** invoke this hook — they keep their existing lifecycle semantics.
79 async fn on_directive(&mut self, _ctx: &mut Context<Self>, _directive: &Directive) {}
80
81 /// The supervisor strategy this actor applies to its own children.
82 fn supervisor_strategy(&self) -> SupervisorStrategy {
83 SupervisorStrategy::default()
84 }
85}