Skip to main content

taktora_executor/
observer.rs

1//! `Observer` trait — lifecycle hooks invoked by the executor.
2
3use crate::error::ExecutorError;
4use crate::fault::{ExecutorFaultReason, FaultReason};
5use crate::task_id::TaskId;
6
7/// Generic user event carried by [`Observer::on_send_event`].
8///
9/// # Construction
10///
11/// Use [`UserEvent::new`] to create a value; struct literal syntax is not
12/// available from outside this crate because `UserEvent` is `#[non_exhaustive]`.
13#[derive(Clone, Debug, Default)]
14#[non_exhaustive]
15pub struct UserEvent {
16    /// User-defined event kind.
17    pub kind: u32,
18    /// Numeric payload.
19    pub int_data: i64,
20    /// Optional string payload.
21    pub string_data: Option<String>,
22}
23
24impl UserEvent {
25    /// Create a new event with the given `kind` and `int_data`.
26    #[must_use]
27    pub const fn new(kind: u32, int_data: i64) -> Self {
28        Self {
29            kind,
30            int_data,
31            string_data: None,
32        }
33    }
34
35    /// Attach an optional string payload to this event.
36    #[must_use]
37    pub fn with_string(mut self, s: impl Into<String>) -> Self {
38        self.string_data = Some(s.into());
39        self
40    }
41}
42
43/// Lifecycle observer invoked by the executor at well-defined points.
44///
45/// All methods have no-op defaults. The executor never blocks on observer
46/// callbacks — heavy work should be queued internally.
47pub trait Observer: Send + Sync {
48    /// Called once just before the dispatch loop begins.
49    fn on_executor_up(&self) {}
50    /// Called once just after the dispatch loop finishes cleanly.
51    fn on_executor_down(&self) {}
52    /// Called when the dispatch loop returns an error.
53    fn on_executor_error(&self, _e: &ExecutorError) {}
54
55    /// Called before an item with `app_id().is_some()` runs (per invocation).
56    fn on_app_start(&self, _task: TaskId, _app: u32, _instance: Option<u32>) {}
57    /// Called after such an item runs.
58    fn on_app_stop(&self, _task: TaskId) {}
59    /// Called when an item returns `Err` or panics.
60    fn on_app_error(&self, _task: TaskId, _e: &(dyn std::error::Error + 'static)) {}
61
62    /// Called when an item invokes `Context::send_event`.
63    fn on_send_event(&self, _task: TaskId, _ev: UserEvent) {}
64
65    /// Called once when a task transitions from `Running` to `Faulted`
66    /// (per-task budget overrun, `REQ_0070`). The cascade transition
67    /// triggered by an executor-wide fault does NOT fire this hook —
68    /// see [`Observer::on_executor_fault`]. `REQ_0073`.
69    fn on_task_fault(&self, _task: TaskId, _reason: FaultReason) {}
70
71    /// Called once when a task transitions from `Faulted` back to
72    /// `Running` (manual clear via `Executor::clear_task_fault`).
73    fn on_task_clear(&self, _task: TaskId) {}
74
75    /// Called once when the executor transitions from `Running` to
76    /// `Faulted` (executor-wide iteration budget breach, `REQ_0071`).
77    fn on_executor_fault(&self, _reason: ExecutorFaultReason) {}
78
79    /// Called once when the executor transitions from `Faulted` back
80    /// to `Running` (manual clear via `Executor::clear_executor_fault`).
81    fn on_executor_clear(&self) {}
82}
83
84/// No-op observer used when the user does not configure one.
85pub struct NoopObserver;
86impl Observer for NoopObserver {}