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 { // Added bounds
39    /// Returns the [`MessageAddress`] associated with this agent handle.
40    ///
41    /// This address contains the agent's unique ID (`Ern`) and the sender channel
42    /// connected to its inbox, allowing others to send messages directly to it or
43    /// use it as a return address.
44    fn reply_address(&self) -> MessageAddress;
45
46    /// Creates an [`OutboundEnvelope`] suitable for sending a message from this agent.
47    ///
48    /// The envelope's `return_address` is set to this agent's address.
49    ///
50    /// # Arguments
51    ///
52    /// * `recipient_address`: An optional [`MessageAddress`] for the intended recipient.
53    ///   If `None`, the envelope is created without a specific recipient.
54    fn create_envelope(&self, recipient_address: Option<MessageAddress>) -> OutboundEnvelope;
55
56    /// Returns a clone of the map containing handles to the agent's direct children.
57    ///
58    /// Provides a snapshot of the currently supervised children. Modifications to the
59    /// returned map do not affect the agent's actual children list.
60    fn children(&self) -> DashMap<String, AgentHandle>;
61
62    /// Attempts to find a direct child agent supervised by this agent, identified by its `Ern`.
63    ///
64    /// # Arguments
65    ///
66    /// * `id`: The unique [`Ern`] of the child agent to locate.
67    ///
68    /// # Returns
69    ///
70    /// * `Some(AgentHandle)`: If a direct child with the matching `Ern` is found.
71    /// * `None`: If no direct child with the specified `Ern` exists.
72    fn find_child(&self, id: &Ern) -> Option<AgentHandle>;
73
74    /// Returns a clone of the agent's [`TaskTracker`].
75    ///
76    /// The tracker can be used to monitor the agent's main task and potentially
77    /// other associated asynchronous operations.
78    fn tracker(&self) -> TaskTracker;
79
80    /// Returns a clone of the agent's unique identifier ([`Ern`]).
81    fn id(&self) -> Ern;
82
83    /// Returns the agent's root name (the first segment of its [`Ern`]) as a `String`.
84    fn name(&self) -> String;
85
86    /// Creates and returns a clone of this agent handle.
87    fn clone_ref(&self) -> AgentHandle; // Consider renaming to `clone_handle` or just relying on `Clone`
88
89    /// Sends a message asynchronously to this agent handle's associated agent.
90    ///
91    /// This default implementation creates an envelope with no specific recipient
92    /// (implying the message is sent to the agent represented by `self`) and uses
93    /// the envelope's `send` method.
94    ///
95    /// # Arguments
96    ///
97    /// * `message`: The message payload to send. Must implement [`ActonMessage`].
98    #[instrument(skip(self, message), fields(message_type = std::any::type_name_of_val(&message)))]
99    fn send(
100        &self,
101        message: impl ActonMessage,
102    ) -> impl Future<Output = ()> + Send + Sync + '_
103    where
104        Self: Sync, // Required by the async move block
105    {
106        async move {
107            // Creates an envelope targeting self.
108            let envelope = self.create_envelope(Some(self.reply_address()));
109            trace!(sender = %self.id(), recipient = %self.id(), "Default send implementation");
110            envelope.send(message).await;
111        }
112    }
113
114    /// Sends a message synchronously to a specified recipient agent.
115    ///
116    /// **Warning:** This default implementation uses [`OutboundEnvelope::reply`], which internally
117    /// spawns a blocking task and creates a new Tokio runtime. This is generally **discouraged**
118    /// and can lead to performance issues or deadlocks, especially if called from within an
119    /// existing asynchronous context. Prefer using asynchronous methods like [`AgentHandleInterface::send`]
120    /// or [`OutboundEnvelope::send`] where possible.
121    ///
122    /// This method wraps the message in a [`BrokerRequest`] before sending via the envelope's
123    /// `reply` method, which seems potentially incorrect unless the intent is specifically
124    /// to interact with the broker synchronously via the recipient. Consider if a direct
125    /// synchronous send mechanism is needed or if the asynchronous `send` should be used.
126    ///
127    /// # Arguments
128    ///
129    /// * `message`: The message payload to send. Must implement [`ActonMessage`].
130    /// * `recipient`: A reference to the [`AgentHandle`] of the recipient agent.
131    ///
132    /// # Returns
133    ///
134    /// A `Result` indicating success or failure. Currently, it relies on the behavior of
135    /// [`OutboundEnvelope::reply`], which might not propagate all underlying errors.
136    fn send_sync(&self, message: impl ActonMessage, recipient: &AgentHandle) -> anyhow::Result<()>
137    where
138        Self: Sized, // Required for calling create_envelope on self
139    {
140        trace!(sender = %self.id(), recipient = %recipient.id(), "Sending message synchronously");
141        let envelope = self.create_envelope(Some(recipient.reply_address()));
142        // Warning: Wrapping the message in BrokerRequest here might be unintended.
143        // If the goal is just to send `message` to `recipient`, it should likely be:
144        // envelope.reply(message)?;
145        envelope.reply(BrokerRequest::new(message))?; // Uses the potentially problematic OutboundEnvelope::reply
146        Ok(())
147    }
148
149    /// Initiates a graceful shutdown of the agent associated with this handle.
150    ///
151    /// This method should send a termination signal (e.g., [`SystemSignal::Terminate`])
152    /// to the agent and wait for its main task and associated tasks (tracked by `tracker`)
153    /// to complete.
154    ///
155    /// # Returns
156    ///
157    /// A `Future` that resolves to `Ok(())` upon successful termination, or an `Err`
158    /// if sending the termination signal or waiting for completion fails.
159    fn stop(&self) -> impl Future<Output = anyhow::Result<()>> + Send + Sync + '_;
160}