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}