mod handler;
mod interceptor;
mod internal;
use crate::client::internal::InternalClient;
use crate::protocol::frame::{Abort, Ack, Begin, Commit, Nack, Send, Subscribe};
use crate::protocol::{Frame, ServerCommand};
use std::error::Error;
use std::fmt::{Display, Formatter};
use std::sync::Arc;
use tokio::sync::mpsc::Sender;
use uuid::Uuid;
type ReceiptId = String;
pub struct Transaction {
transaction_id: String,
internal_client: Arc<InternalClient>,
}
impl Transaction {
fn new(transaction_id: String, internal_client: Arc<InternalClient>) -> Self {
Self {
transaction_id,
internal_client,
}
}
pub async fn send(&self, send: Send) -> Result<(), Box<dyn Error>> {
self.internal_client
.send(send.header("transaction", self.transaction_id.clone()))
.await
}
pub async fn ack(&self, ack: Ack) -> Result<(), Box<dyn Error>> {
self.internal_client
.ack(ack.transaction(self.transaction_id.clone()))
.await
}
pub async fn nack(&self, nack: Nack) -> Result<(), Box<dyn Error>> {
self.internal_client
.nack(nack.transaction(self.transaction_id.clone()))
.await
}
pub async fn commit(&self) -> Result<(), Box<dyn Error>> {
self.internal_client
.emit(
Commit::new(self.transaction_id.clone())
.receipt(Uuid::new_v4().to_string())
.into(),
)
.await
}
pub async fn abort(&self) -> Result<(), Box<dyn Error>> {
self.internal_client
.emit(
Abort::new(self.transaction_id.clone())
.receipt(Uuid::new_v4().to_string())
.into(),
)
.await
}
}
pub struct Client {
internal_client: Arc<InternalClient>,
}
pub struct ClientBuilder {
host: String,
heartbeat: Option<(u32, u32)>,
}
impl ClientBuilder {
pub fn new<A: Into<String>>(host: A) -> Self {
Self {
host: host.into(),
heartbeat: None,
}
}
pub fn heartbeat(mut self, client_interval: u32, server_interval: u32) -> Self {
self.heartbeat = Some((client_interval, server_interval));
self
}
}
#[derive(Debug)]
pub enum ClientError {
ReceiptTimeout(String),
Nack(String),
ConnectionError(Option<Box<dyn Error>>),
}
impl Display for ClientError {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "Client error")
}
}
impl Error for ClientError {}
impl Client {
pub async fn connect(builder: ClientBuilder) -> Result<Self, Box<dyn Error>> {
let internal_client = InternalClient::connect(builder).await?;
Ok(Self {
internal_client: Arc::new(internal_client),
})
}
pub async fn subscribe(
&self,
subscribe: Subscribe,
sender: Sender<Frame<ServerCommand>>,
) -> Result<(), Box<dyn Error>> {
self.internal_client.subscribe(subscribe, sender).await
}
pub async fn send(&self, send: Send) -> Result<(), Box<dyn Error>> {
self.internal_client.send(send).await
}
pub async fn ack(&self, ack: Ack) -> Result<(), Box<dyn Error>> {
self.internal_client.ack(ack).await
}
pub async fn nack(&self, nack: Nack) -> Result<(), Box<dyn Error>> {
self.internal_client.nack(nack).await
}
pub async fn begin(&self) -> Result<Transaction, Box<dyn Error>> {
let transaction_id = Uuid::new_v4();
let receipt_id = Uuid::new_v4();
self.internal_client
.emit(
Begin::new(transaction_id.to_string())
.receipt(receipt_id.to_string())
.into(),
)
.await?;
Ok(Transaction {
transaction_id: transaction_id.to_string(),
internal_client: Arc::clone(&self.internal_client),
})
}
}
impl Frame<ServerCommand> {
pub fn ack(&self) -> Option<Ack> {
if let ServerCommand::Message = self.command {
if let Some(ack) = self.headers.get("ack") {
return Some(Ack::new(ack));
}
}
None
}
pub fn nack(&self) -> Option<Nack> {
if let ServerCommand::Message = self.command {
if let Some(ack) = self.headers.get("ack") {
return Some(Nack::new(ack));
}
}
None
}
}