You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

132 lines
4.0 KiB

use std::collections::HashMap;
use networking::serverstate::ClientId;
use items;
use async_std::sync::{Arc, Mutex, MutexGuard};
use futures::future::{Future, OptionFuture};
use items::trade::TradeItem;
#[derive(Debug, Clone, Eq, PartialEq)]
pub enum TradeStatus {
SentRequest,
ReceivedRequest,
Trading,
Confirmed,
FinalConfirm,
ItemsChecked,
TradeComplete,
}
#[derive(Debug, Clone)]
pub struct ClientTradeState {
client: ClientId,
other_client: ClientId,
pub items: Vec<TradeItem>,
pub meseta: usize,
pub status: TradeStatus,
}
impl ClientTradeState {
pub fn client(&self) -> ClientId {
self.client
}
pub fn other_client(&self) -> ClientId {
self.other_client
}
}
#[derive(thiserror::Error, Debug)]
pub enum TradeStateError {
#[error("client not in trade {0}")]
ClientNotInTrade(ClientId),
#[error("mismatched trade {0} {1}")]
MismatchedTrade(ClientId, ClientId),
}
#[derive(Default, Debug, Clone)]
pub struct TradeState {
trades: HashMap<ClientId, Arc<Mutex<ClientTradeState>>>,
}
impl TradeState {
pub fn new_trade(&mut self, sender: &ClientId, receiver: &ClientId) {
let state = ClientTradeState {
client: *sender,
other_client: *receiver,
items: Default::default(),
meseta: 0,
status: TradeStatus::SentRequest,
};
self.trades.insert(*sender, Arc::new(Mutex::new(state)));
let state = ClientTradeState {
client: *receiver,
other_client: *sender,
items: Default::default(),
meseta: 0,
status: TradeStatus::ReceivedRequest,
};
self.trades.insert(*receiver, Arc::new(Mutex::new(state)));
}
pub fn in_trade(&self, client: &ClientId) -> bool {
self.trades.contains_key(client)
}
/*
pub async fn with<'a, T, F, Fut> (&'a self, client: &ClientId, func: F) -> Result<T, TradeStateError>
where
F: FnOnce(MutexGuard<'a, ClientTradeState>, MutexGuard<'a, ClientTradeState>) -> Fut + 'a,
Fut: Future<Output=T>
{
let c1 = self.trades.get(client).ok_or(TradeStateError::ClientNotInTrade(*client))?.lock().await;
let c2 = self.trades.get(&c1.other_client).ok_or(TradeStateError::ClientNotInTrade(c1.other_client))?.lock().await;
// sanity check
if c1.client != c2.other_client {
return Err(TradeStateError::MismatchedTrade(c1.client, c2.client));
}
Ok(func(c1, c2).await)
}
*/
pub async fn with<'a, T, F, Fut> (&'a self, client: &ClientId, func: F) -> Result<T, TradeStateError>
where
T: Send,
//F: for<'b> FnOnce(&'b mut ClientTradeState, &'b mut ClientTradeState) -> BoxFuture<'b, T> + Send + 'a
//F: for<'b> FnOnce(&'b mut ClientTradeState, &'b mut ClientTradeState) -> Fut + Send + 'a,
F: FnOnce(MutexGuard<'a, ClientTradeState>, MutexGuard<'a, ClientTradeState>) -> Fut + 'a,
Fut: Future<Output = T>,
{
let c1 = self.trades
.get(client)
.ok_or(TradeStateError::ClientNotInTrade(*client))?
.lock()
.await;
let c2 = self.trades
.get(&c1.other_client)
.ok_or(TradeStateError::ClientNotInTrade(c1.other_client))?
.lock()
.await;
if c1.client != c2.other_client {
return Err(TradeStateError::MismatchedTrade(c1.client, c2.client));
}
Ok(func(c1, c2).await)
}
// TODO: is it possible for this to not return Options?
pub async fn remove_trade(&mut self, client: &ClientId) -> (Option<ClientTradeState>, Option<ClientTradeState>) {
let c1 = OptionFuture::from(self.trades.remove(client).map(|c| async move {c.lock().await.clone()} )).await;
let c2 = if let Some(ref state) = c1 {
OptionFuture::from(self.trades.remove(&state.other_client).map(|c| async move {c.lock().await.clone()})).await
}
else {
None
};
(c1, c2)
}
}