#[cfg(test)]
mod agent_test;
use crate::errors::*;
use crate::message::*;
use util::Error;
use tokio::sync::mpsc;
use tokio::time::Instant;
use std::collections::HashMap;
use std::sync::Arc;
use crate::client::ClientTransaction;
use rand::Rng;
pub type Handler = Option<Arc<mpsc::UnboundedSender<Event>>>;
pub fn noop_handler() -> Handler {
None
}
pub struct Agent {
transactions: HashMap<TransactionId, AgentTransaction>,
closed: bool, handler: Handler, }
#[derive(Debug, Clone)]
pub enum EventType {
CALLBACK(TransactionId),
INSERT(ClientTransaction),
REMOVE(TransactionId),
CLOSE,
}
impl Default for EventType {
fn default() -> Self {
EventType::CALLBACK(TransactionId::default())
}
}
#[derive(Debug, Clone)]
pub struct Event {
pub event_type: EventType,
pub event_body: Result<Message, Error>,
}
impl Default for Event {
fn default() -> Self {
Event {
event_type: EventType::default(),
event_body: Ok(Message::default()),
}
}
}
pub(crate) struct AgentTransaction {
id: TransactionId,
deadline: Instant,
}
const AGENT_COLLECT_CAP: usize = 100;
#[derive(PartialEq, Eq, Hash, Copy, Clone, Default, Debug)]
pub struct TransactionId(pub [u8; TRANSACTION_ID_SIZE]);
impl TransactionId {
pub fn new() -> Self {
let mut b = TransactionId([0u8; TRANSACTION_ID_SIZE]);
rand::thread_rng().fill(&mut b.0);
b
}
}
impl Setter for TransactionId {
fn add_to(&self, m: &mut Message) -> Result<(), Error> {
m.transaction_id = *self;
m.write_transaction_id();
Ok(())
}
}
pub enum ClientAgent {
Process(Message),
Collect(Instant),
Start(TransactionId, Instant),
Stop(TransactionId),
Close,
}
impl Agent {
pub fn new(handler: Handler) -> Self {
Agent {
transactions: HashMap::new(),
closed: false,
handler,
}
}
fn stop_with_error(&mut self, id: TransactionId, error: Error) -> Result<(), Error> {
if self.closed {
return Err(ERR_AGENT_CLOSED.clone());
}
let v = self.transactions.remove(&id);
if let Some(t) = v {
if let Some(handler) = &self.handler {
handler.send(Event {
event_type: EventType::CALLBACK(t.id),
event_body: Err(error),
})?;
}
Ok(())
} else {
Err(ERR_TRANSACTION_NOT_EXISTS.clone())
}
}
fn process(&mut self, message: Message) -> Result<(), Error> {
if self.closed {
return Err(ERR_AGENT_CLOSED.clone());
}
self.transactions.remove(&message.transaction_id);
let e = Event {
event_type: EventType::CALLBACK(message.transaction_id),
event_body: Ok(message),
};
if let Some(handler) = &self.handler {
handler.send(e)?;
}
Ok(())
}
fn close(&mut self) -> Result<(), Error> {
if self.closed {
return Err(ERR_AGENT_CLOSED.clone());
}
for id in self.transactions.keys() {
let e = Event {
event_type: EventType::CALLBACK(*id),
event_body: Err(ERR_AGENT_CLOSED.clone()),
};
if let Some(handler) = &self.handler {
handler.send(e)?;
}
}
self.transactions = HashMap::new();
self.closed = true;
self.handler = noop_handler();
Ok(())
}
fn start(&mut self, id: TransactionId, deadline: Instant) -> Result<(), Error> {
if self.closed {
return Err(ERR_AGENT_CLOSED.clone());
}
if self.transactions.contains_key(&id) {
return Err(ERR_TRANSACTION_EXISTS.clone());
}
self.transactions
.insert(id, AgentTransaction { id, deadline });
Ok(())
}
fn stop(&mut self, id: TransactionId) -> Result<(), Error> {
self.stop_with_error(id, ERR_TRANSACTION_STOPPED.clone())
}
fn collect(&mut self, deadline: Instant) -> Result<(), Error> {
if self.closed {
return Err(ERR_AGENT_CLOSED.clone());
}
let mut to_remove: Vec<TransactionId> = Vec::with_capacity(AGENT_COLLECT_CAP);
for (id, t) in &self.transactions {
if t.deadline < deadline {
to_remove.push(*id);
}
}
for id in &to_remove {
self.transactions.remove(id);
}
for id in to_remove {
let event = Event {
event_type: EventType::CALLBACK(id),
event_body: Err(ERR_TRANSACTION_TIME_OUT.clone()),
};
if let Some(handler) = &self.handler {
handler.send(event)?;
}
}
Ok(())
}
fn set_handler(&mut self, h: Handler) -> Result<(), Error> {
if self.closed {
return Err(ERR_AGENT_CLOSED.clone());
}
self.handler = h;
Ok(())
}
pub async fn run(mut agent: Agent, mut rx: mpsc::Receiver<ClientAgent>) {
while let Some(client_agent) = rx.recv().await {
let result = match client_agent {
ClientAgent::Process(message) => agent.process(message),
ClientAgent::Collect(deadline) => agent.collect(deadline),
ClientAgent::Start(tid, deadline) => agent.start(tid, deadline),
ClientAgent::Stop(tid) => agent.stop(tid),
ClientAgent::Close => agent.close(),
};
if let Err(err) = result {
if err == *ERR_AGENT_CLOSED {
break;
}
}
}
}
}