use prost::Message;
use std::sync::Arc;
use tokio::sync::oneshot;
use uuid::Uuid;
use crate::{
proto::autobahn::{RpcMessageType, RpcRequestMessage, RpcResponseMessage, RpcResponseType},
rpc::RPCError,
util::deserialize_partial,
};
use crate::{autobahn::Autobahn, AUTOBAHN_RPC_OUTPUT_PREFIX, AUTOBAHN_RPC_PREFIX};
impl Autobahn {
pub async fn call_rpc<OutputType: prost::Message + Default, ArgsType: prost::Message>(
self: &Arc<Self>,
fn_rpc_id: String,
args: ArgsType,
timeout_ms: u64,
) -> Result<OutputType, RPCError> {
let input_args_bytes = args.encode_to_vec();
let call_id = Self::generate_call_id();
let rpc_request_message = RpcRequestMessage {
message_type: RpcMessageType::RpcRequest as i32,
call_id: call_id.clone(),
payload: input_args_bytes,
};
let input_args_topic = self.from_function_id_to_rpc_publish_topic(&fn_rpc_id);
let subscribe_topic = self.from_function_id_to_rpc_subscribe_topic(&call_id);
let (tx, rx) = oneshot::channel::<Vec<u8>>();
let tx_arc = Arc::new(tokio::sync::Mutex::new(Some(tx)));
let make_cb = |arc: Arc<tokio::sync::Mutex<Option<oneshot::Sender<Vec<u8>>>>>| {
move |payload: Vec<u8>| {
let arc = arc.clone();
async move {
if let Some(sender) = arc.lock().await.take() {
let _ = sender.send(payload);
}
}
}
};
let subscribe_callback = make_cb(tx_arc.clone());
if let Err(e) = self.subscribe(&subscribe_topic, subscribe_callback).await {
return Err(RPCError::ConnectionError(e.to_string()));
}
let output = self
.publish(
input_args_topic.as_str(),
rpc_request_message.encode_to_vec(),
)
.await;
match output {
Ok(_) => {}
Err(e) => {
return Err(RPCError::ConnectionError(e.to_string()));
}
}
let response = tokio::time::timeout(std::time::Duration::from_millis(timeout_ms), async {
let bytes = rx
.await
.map_err(|e| RPCError::Other(format!("Receive error: {}", e)))?;
let _ = self.unsubscribe(&subscribe_topic).await;
RpcResponseMessage::decode(&bytes[..]).map_err(|e| {
RPCError::DeserializationError(format!("Failed to decode response: {}", e))
})
})
.await;
if response.is_err() {
let _ = self.unsubscribe(&subscribe_topic).await;
return Err(RPCError::TimeoutError(response.err().unwrap().to_string()));
}
let response = response.unwrap()?;
match response.response_type {
x if x == RpcResponseType::RpcResponseSuccess as i32 => {
return deserialize_partial(&*response.payload).map_err(|e| {
RPCError::DeserializationError(format!("Failed to decode response: {}", e))
});
}
x if x == RpcResponseType::RpcResponseError as i32 => {
return Err(RPCError::ErrorResponse(format!(
"RPC error response: {:?}",
response.payload
)));
}
_ => return Err(RPCError::Other("Unknown response type".to_string())), }
}
fn generate_call_id() -> String {
Uuid::new_v4().to_string()
}
fn from_function_id_to_rpc_subscribe_topic(&self, call_id: &str) -> String {
format!("{}{}", AUTOBAHN_RPC_OUTPUT_PREFIX, call_id)
}
fn from_function_id_to_rpc_publish_topic(&self, fn_rpc_id: &str) -> String {
format!("{}{}", AUTOBAHN_RPC_PREFIX, fn_rpc_id)
}
}