acton_core/actor/managed_agent.rs
1/*
2 * Copyright (c) 2024. Govcraft
3 *
4 * Licensed under either of
5 * * Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
8 * * MIT license: http://opensource.org/licenses/MIT
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the applicable License for the specific language governing permissions and
14 * limitations under that License.
15 */
16
17use std::fmt;
18use std::fmt::Debug;
19use std::fmt::Formatter;
20
21use acton_ern::prelude::*;
22use tokio::sync::mpsc::Receiver;
23use tokio_util::task::TaskTracker;
24
25pub use idle::Idle;
26
27use crate::common::{
28 AgentHandle, AsyncLifecycleHandler, BrokerRef, HaltSignal, ParentRef, ReactorMap,
29};
30use crate::message::Envelope;
31use crate::prelude::AgentRuntime;
32
33mod idle;
34/// Contains the `Started` type-state marker and associated implementations for running agents.
35pub mod started;
36
37/// Represents an agent whose lifecycle and message processing are managed by the Acton framework.
38///
39/// `ManagedAgent` acts as a runtime wrapper around user-defined agent logic and state (`Model`).
40/// It utilizes a type-state pattern via the `AgentState` generic parameter (e.g., [`Idle`], [`started::Started`])
41/// to enforce valid operations during different phases of the agent's lifecycle (configuration vs. active processing).
42///
43/// The framework handles the underlying task spawning, message reception from the `inbox`,
44/// dispatching messages to registered `message_handlers`, executing lifecycle hooks
45/// (like `before_start`, `after_stop`), and managing graceful shutdown via the `halt_signal`.
46///
47/// Users typically interact with `ManagedAgent` indirectly through an [`AgentHandle`] after the agent
48/// has been started, or directly during the configuration phase (when in the [`Idle`] state)
49/// to register message handlers and lifecycle hooks.
50///
51/// # Type Parameters
52///
53/// * `AgentState`: A marker type (e.g., [`Idle`], [`started::Started`]) indicating the current lifecycle state of the agent.
54/// * `Model`: The user-defined type containing the agent's state and associated logic. It must
55/// implement `Default`, `Send`, `Debug`, and be `'static`. Message handlers and lifecycle
56/// hooks operate on an instance of this `Model`.
57pub struct ManagedAgent<AgentState, Model: Default + Send + Debug + 'static> {
58 /// Handle for external interaction with this agent.
59 pub(crate) handle: AgentHandle,
60
61 /// Optional handle to the parent (supervisor) agent.
62 pub(crate) parent: Option<ParentRef>,
63
64 /// Handle to the system message broker.
65 pub(crate) broker: BrokerRef,
66
67 /// Signal used for initiating graceful shutdown.
68 pub(crate) halt_signal: HaltSignal,
69
70 /// The agent's unique and potentially hierarchical identifier.
71 pub(crate) id: Ern,
72 /// Reference to the Acton system runtime.
73 pub(crate) runtime: AgentRuntime,
74
75 /// The user-defined state and logic associated with this agent.
76 ///
77 /// This field holds the instance of the type provided as the `Model` generic
78 /// parameter. Message handlers (registered via `act_on` in the [`Idle`] state)
79 /// and lifecycle hooks (e.g., `before_start`, `after_stop`) receive mutable
80 /// access to this `model` to implement the agent's specific behavior and manage its data.
81 pub model: Model,
82
83 /// Tracks associated Tokio tasks, primarily the agent's main loop.
84 pub(crate) tracker: TaskTracker,
85
86 /// MPSC receiver for incoming messages.
87 pub(crate) inbox: Receiver<Envelope>,
88 /// Asynchronous hook executed before the agent starts its message loop.
89 pub(crate) before_start: AsyncLifecycleHandler<Model>,
90 /// Asynchronous hook executed after the agent starts its message loop.
91 pub(crate) after_start: AsyncLifecycleHandler<Model>,
92 /// Asynchronous hook executed just before the agent stops its message loop.
93 pub(crate) before_stop: AsyncLifecycleHandler<Model>,
94 /// Asynchronous hook executed after the agent stops its message loop.
95 pub(crate) after_stop: AsyncLifecycleHandler<Model>,
96 /// Map storing registered mutable message handlers (`TypeId` -> handler function).
97 pub(crate) message_handlers: ReactorMap<Model>,
98 /// Map storing registered read-only message handlers (`TypeId` -> handler function).
99 pub(crate) read_only_handlers: ReactorMap<Model>,
100 /// Map storing registered error handlers (`TypeId` -> error handler closure, wrapped in Arc for clone safety).
101 pub(crate) error_handler_map: std::collections::HashMap<
102 (std::any::TypeId, std::any::TypeId),
103 Box<crate::common::ErrorHandler<Model>>,
104 >,
105 /// Tokio cancellation token for managing shutdown/cancellation.
106 /// This will be set to Some(token) when the agent is created, by cloning the root token.
107 pub(crate) cancellation_token: Option<tokio_util::sync::CancellationToken>,
108 /// Phantom data to associate the `AgentState` type parameter.
109 _actor_state: std::marker::PhantomData<AgentState>,
110}
111
112// implement getter functions for ManagedAgent
113impl<AgentState, Model: Default + Send + Debug + 'static> ManagedAgent<AgentState, Model> {
114 /// Returns a reference to the agent's unique identifier (`Ern`).
115 #[inline]
116 pub fn id(&self) -> &Ern {
117 &self.id
118 }
119
120 /// Returns the root name segment of the agent's identifier (`Ern`).
121 #[inline]
122 pub fn name(&self) -> &str {
123 self.id.root.as_str()
124 }
125
126 /// Returns a reference to the agent's [`AgentHandle`].
127 ///
128 /// The handle is the primary means for external interaction with the agent
129 /// once it has started.
130 #[inline]
131 pub fn handle(&self) -> &AgentHandle {
132 &self.handle
133 }
134
135 /// Returns a reference to the optional parent agent's handle ([`ParentRef`]).
136 ///
137 /// Returns `None` if this is a top-level agent.
138 #[inline]
139 pub fn parent(&self) -> &Option<ParentRef> {
140 &self.parent
141 }
142
143 /// Returns a reference to the system message broker's handle ([`BrokerRef`]).
144 #[inline]
145 pub fn broker(&self) -> &BrokerRef {
146 &self.broker
147 }
148
149 /// Returns a reference to the [`AgentRuntime`] this agent belongs to.
150 #[inline]
151 pub fn runtime(&self) -> &AgentRuntime {
152 &self.runtime
153 }
154}
155
156impl<AgentState, Model: Default + Send + Debug + 'static> Debug
157 for ManagedAgent<AgentState, Model>
158{
159 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
160 f.debug_struct("ManagedAgent")
161 .field("id", &self.id) // Changed from "key" to "id" for clarity
162 .field("model", &self.model) // Optionally include model debug info
163 .field("parent", &self.parent)
164 .field("broker", &self.broker)
165 // Avoid showing channels/handlers in Debug output
166 .finish_non_exhaustive() // Indicate not all fields are shown
167 }
168}