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}