use std::collections::HashMap;
use std::fmt::Debug;
use std::sync::Arc;
use serde::{de::DeserializeOwned, Serialize};
use tokio::sync::{oneshot, Mutex};
use uuid::Uuid;
use crate::{
ipc_types::ModuleToOrchestrator,
message::{EncodedMessage, Message, MessageError, EncodingFormat},
communication::{
MessageChannel,
TcpChannel,
},
Error as SdkError,
};
#[derive(thiserror::Error, Debug)]
pub enum InternalMessagingError {
#[error("Failed to serialize request payload: {0}")]
SerializationError(#[from] MessageError),
#[error("Network error while sending message to orchestrator: {0}")]
NetworkError(SdkError),
#[error("Request timed out while waiting for response from target module")]
Timeout,
#[error("Orchestrator indicated target module '{0}' was not found")]
TargetModuleNotFound(String),
#[error("Orchestrator indicated target endpoint '{0}' on module '{1}' was not found")]
TargetEndpointNotFound(String, String),
#[error("Target module responded with an application error: {0}")]
ApplicationError(String),
#[error("Failed to deserialize response payload: {0}")]
DeserializationError(MessageError),
#[error("Internal SDK error: {0}")]
InternalSDKError(String),
#[error("Orchestrator error: {0}")]
OrchestratorError(String),
}
impl From<SdkError> for InternalMessagingError {
fn from(e: SdkError) -> Self {
InternalMessagingError::NetworkError(e)
}
}
impl From<MessageError> for SdkError {
fn from(e: MessageError) -> Self {
SdkError::Config(crate::error::ConfigError::Invalid(e.to_string()))
}
}
pub type InternalMessagingResult<T> = Result<T, InternalMessagingError>;
pub(crate) type PendingInternalResponses =
Arc<Mutex<HashMap<Uuid, oneshot::Sender<Result<EncodedMessage, SdkError>>>>>;
#[derive(Clone, Debug)]
pub struct InternalMessagingClient {
orchestrator_channel: Arc<TcpChannel>, pending_responses: PendingInternalResponses,
default_encoding: EncodingFormat,
}
impl InternalMessagingClient {
#[cfg(feature = "ipc_channel")]
pub fn new_for_ipc_only(
module_id: String,
pending_responses: Option<PendingInternalResponses>,
) -> Self {
tracing::info!("Creating InternalMessagingClient for IPC-only operation for module {}", module_id);
let responses = pending_responses.unwrap_or_else(|| {
Arc::new(Mutex::new(HashMap::new()))
});
let config = crate::tcp_types::ConnectionConfig::new("127.0.0.1".to_string(), 0);
let dummy_channel = TcpChannel::new(config);
Self {
orchestrator_channel: Arc::new(dummy_channel),
pending_responses: responses,
default_encoding: EncodingFormat::Json,
}
}
#[cfg(feature = "ipc_channel")]
pub fn new(
module_id: String,
pending_responses: Option<PendingInternalResponses>,
orchestrator_channel: Option<Arc<TcpChannel>>,
) -> Self {
let responses = pending_responses.unwrap_or_else(|| {
Arc::new(Mutex::new(HashMap::new()))
});
let channel = orchestrator_channel.unwrap_or_else(|| {
tracing::warn!(
module_id = %module_id,
"Creating InternalMessagingClient without a valid orchestrator_channel, creating fallback"
);
let fallback_config = crate::tcp_types::ConnectionConfig::new(
"127.0.0.1".to_string(),
65535 ).with_timeout(std::time::Duration::from_secs(30));
Arc::new(TcpChannel::new(fallback_config))
});
Self {
orchestrator_channel: channel,
pending_responses: responses,
default_encoding: EncodingFormat::Json,
}
}
#[cfg(not(feature = "ipc_channel"))]
pub fn new(
_module_id: String,
pending_responses: Option<PendingInternalResponses>,
) -> Self {
let responses = pending_responses.unwrap_or_else(|| {
Arc::new(Mutex::new(HashMap::new()))
});
Self {
orchestrator_channel: Arc::new(TcpChannel::new(Default::default())),
pending_responses: responses,
default_encoding: EncodingFormat::Json,
}
}
pub async fn send_request<
Req: Serialize + Debug + Clone,
Res: DeserializeOwned + Debug,
>(
&self,
target_module_id: String,
target_endpoint: String,
request_payload: Req,
_timeout: Option<std::time::Duration>, ) -> InternalMessagingResult<Res> {
let request_id = Uuid::new_v4();
let message = Message::new(request_payload);
let encoded_payload = EncodedMessage::encode_with_format(&message, self.default_encoding)
.map_err(InternalMessagingError::SerializationError)?;
let orchestrator_message = ModuleToOrchestrator::RouteToModule {
target_module_id,
target_endpoint,
request_id,
payload: encoded_payload,
};
let (tx, rx) = oneshot::channel();
{
let mut pending = self.pending_responses.lock().await;
pending.insert(request_id, tx);
}
let outer_message = Message::new(orchestrator_message); let encoded_orchestrator_message = EncodedMessage::encode_with_format(&outer_message, self.default_encoding)
.map_err(|e| InternalMessagingError::InternalSDKError(format!("Failed to encode orchestrator message: {}", e)))?;
self.orchestrator_channel
.send(encoded_orchestrator_message)
.await
.map_err(|e| InternalMessagingError::NetworkError(e.into()))?;
let timeout_duration = _timeout.unwrap_or(std::time::Duration::from_secs(30));
match tokio::time::timeout(timeout_duration, rx).await {
Ok(channel_result) => {
match channel_result {
Ok(Ok(encoded_response_payload)) => {
let response: Res = match encoded_response_payload.format() {
crate::message::EncodingFormat::Json => {
let json_str = std::str::from_utf8(encoded_response_payload.data())
.map_err(|e| InternalMessagingError::DeserializationError(
MessageError::InvalidFormat(e.to_string())
))?;
serde_json::from_str(json_str)
.map_err(|e| InternalMessagingError::DeserializationError(MessageError::JsonSerializationError(e)))?
},
_ => {
let json_encoded = encoded_response_payload.to_format(crate::message::EncodingFormat::Json)
.map_err(InternalMessagingError::DeserializationError)?;
let json_str = std::str::from_utf8(json_encoded.data())
.map_err(|e| InternalMessagingError::DeserializationError(
MessageError::InvalidFormat(e.to_string())
))?;
serde_json::from_str(json_str)
.map_err(|e| InternalMessagingError::DeserializationError(MessageError::JsonSerializationError(e)))?
}
};
Ok(response)
}
Ok(Err(sdk_error)) => {
Err(InternalMessagingError::OrchestratorError(sdk_error.to_string()))
}
Err(_oneshot_cancelled) => {
Err(InternalMessagingError::InternalSDKError(
"Response channel closed prematurely; SDK might be shutting down".to_string(),
))
}
}
}
Err(_timeout_elapsed) => {
{
let mut pending = self.pending_responses.lock().await;
pending.remove(&request_id);
}
Err(InternalMessagingError::Timeout)
}
}
}
}
#[allow(dead_code)]
pub(crate) async fn process_routed_module_response(
response_id: Uuid,
response_payload_or_error: Result<EncodedMessage, SdkError>,
pending_responses: PendingInternalResponses,
) {
if let Some(tx) = pending_responses.lock().await.remove(&response_id) {
if let Err(_e) = tx.send(response_payload_or_error) {
tracing::warn!(
request_id = %response_id,
"Failed to send routed module response to waiting task; receiver was dropped"
);
}
} else {
tracing::warn!(
request_id = %response_id,
"Received routed module response for an unknown or already handled request ID"
);
}
}