acton_core/traits/
agent_handle_interface.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::Debug; // Import Debug
18use std::future::Future;
19
20use acton_ern::Ern;
21use async_trait::async_trait;
22use dashmap::DashMap;
23use tokio_util::task::TaskTracker;
24use tracing::{instrument, trace}; // Removed error, warn as they weren't used in defaults
25
26use crate::common::*; // Keep wildcard import if necessary, or specify types
27use crate::message::{BrokerRequest, MessageAddress}; // BrokerRequest used in send_sync default
28use crate::traits::acton_message::ActonMessage;
29
30/// Defines the core asynchronous interface for interacting with an agent via its handle.
31///
32/// This trait specifies the fundamental operations that can be performed on an agent's handle,
33/// such as sending messages, managing its lifecycle, accessing identity information,
34/// and navigating the supervision hierarchy. It is typically implemented by [`AgentHandle`].
35///
36/// Implementors of this trait provide the concrete mechanisms for these operations.
37#[async_trait]
38pub trait AgentHandleInterface: Send + Sync + Debug + Clone + 'static {
39    // Added bounds
40    /// Returns the [`MessageAddress`] associated with this agent handle.
41    ///
42    /// This address contains the agent's unique ID (`Ern`) and the sender channel
43    /// connected to its inbox, allowing others to send messages directly to it or
44    /// use it as a return address.
45    fn reply_address(&self) -> MessageAddress;
46
47    /// Creates an [`OutboundEnvelope`] suitable for sending a message from this agent.
48    ///
49    /// The envelope's `return_address` is set to this agent's address.
50    ///
51    /// # Arguments
52    ///
53    /// * `recipient_address`: An optional [`MessageAddress`] for the intended recipient.
54    ///   If `None`, the envelope is created without a specific recipient.
55    fn create_envelope(&self, recipient_address: Option<MessageAddress>) -> OutboundEnvelope;
56
57    /// Returns a clone of the map containing handles to the agent's direct children.
58    ///
59    /// Provides a snapshot of the currently supervised children. Modifications to the
60    /// returned map do not affect the agent's actual children list.
61    fn children(&self) -> DashMap<String, AgentHandle>;
62
63    /// Attempts to find a direct child agent supervised by this agent, identified by its `Ern`.
64    ///
65    /// # Arguments
66    ///
67    /// * `id`: The unique [`Ern`] of the child agent to locate.
68    ///
69    /// # Returns
70    ///
71    /// * `Some(AgentHandle)`: If a direct child with the matching `Ern` is found.
72    /// * `None`: If no direct child with the specified `Ern` exists.
73    fn find_child(&self, id: &Ern) -> Option<AgentHandle>;
74
75    /// Returns a clone of the agent's [`TaskTracker`].
76    ///
77    /// The tracker can be used to monitor the agent's main task and potentially
78    /// other associated asynchronous operations.
79    fn tracker(&self) -> TaskTracker;
80
81    /// Returns a clone of the agent's unique identifier ([`Ern`]).
82    fn id(&self) -> Ern;
83
84    /// Returns the agent's root name (the first segment of its [`Ern`]) as a `String`.
85    fn name(&self) -> String;
86
87    /// Creates and returns a clone of this agent handle.
88    fn clone_ref(&self) -> AgentHandle; // Consider renaming to `clone_handle` or just relying on `Clone`
89
90    /// Sends a message asynchronously to this agent handle's associated agent.
91    ///
92    /// This default implementation creates an envelope with no specific recipient
93    /// (implying the message is sent to the agent represented by `self`) and uses
94    /// the envelope's `send` method.
95    ///
96    /// # Arguments
97    ///
98    /// * `message`: The message payload to send. Must implement [`ActonMessage`].
99    #[instrument(skip(self, message), fields(message_type = std::any::type_name_of_val(&message)))]
100    fn send(&self, message: impl ActonMessage) -> impl Future<Output = ()> + Send + Sync + '_
101    where
102        Self: Sync, // Required by the async move block
103    {
104        async move {
105            // Creates an envelope targeting self.
106            let envelope = self.create_envelope(Some(self.reply_address()));
107            trace!(sender = %self.id(), recipient = %self.id(), "Default send implementation");
108            envelope.send(message).await;
109        }
110    }
111
112    /// Sends a message synchronously to a specified recipient agent.
113    ///
114    /// **Warning:** This default implementation uses [`OutboundEnvelope::reply`], which internally
115    /// spawns a blocking task and creates a new Tokio runtime. This is generally **discouraged**
116    /// and can lead to performance issues or deadlocks, especially if called from within an
117    /// existing asynchronous context. Prefer using asynchronous methods like [`AgentHandleInterface::send`]
118    /// or [`OutboundEnvelope::send`] where possible.
119    ///
120    /// # Arguments
121    ///
122    /// * `message`: The message payload to send. Must implement [`ActonMessage`].
123    /// * `recipient`: A reference to the [`AgentHandle`] of the recipient agent.
124    ///
125    /// # Returns
126    ///
127    /// A `Result` indicating success or failure. Currently, it relies on the behavior of
128    /// [`OutboundEnvelope::reply`], which might not propagate all underlying errors.
129    fn send_sync(&self, message: impl ActonMessage, recipient: &AgentHandle) -> anyhow::Result<()>
130    where
131        Self: Sized, // Required for calling create_envelope on self
132    {
133        trace!(sender = %self.id(), recipient = %recipient.id(), "Sending message synchronously");
134        let envelope = self.create_envelope(Some(recipient.reply_address()));
135        envelope.reply(BrokerRequest::new(message))?; // Uses the potentially problematic OutboundEnvelope::reply
136        Ok(())
137    }
138
139    /// Initiates a graceful shutdown of the agent associated with this handle.
140    ///
141    /// This method should send a termination signal (e.g., [`SystemSignal::Terminate`])
142    /// to the agent and wait for its main task and associated tasks (tracked by `tracker`)
143    /// to complete.
144    ///
145    /// # Returns
146    ///
147    /// A `Future` that resolves to `Ok(())` upon successful termination, or an `Err`
148    /// if sending the termination signal or waiting for completion fails.
149    fn stop(&self) -> impl Future<Output = anyhow::Result<()>> + Send + Sync + '_;
150}