use uuid::Uuid;
#[cfg(feature = "server")]
use self::{
group::{Group, GroupId},
parameters::Parameters,
session::{Session, SessionId, SessionKind, SessionPartyNumber, SessionValue},
};
#[cfg(feature = "server")]
use anyhow::Result;
#[cfg(feature = "server")]
use std::collections::HashMap;
#[cfg(feature = "server")]
use thiserror::Error;
#[cfg(feature = "server")]
use tokio::sync::{mpsc::UnboundedSender, RwLock};
pub mod group;
pub mod parameters;
pub mod session;
pub type ClientId = Uuid;
#[derive(Debug, Error)]
#[cfg(feature = "server")]
pub enum StateError {
#[error("group `{0}` not found")]
GroupNotFound(GroupId),
#[error("session `{0}` fro group `{1} not found")]
SessionNotFound(SessionId, GroupId),
#[error("group `{0}` is full")]
GroupIsFull(GroupId),
#[error("party `{0}` not found")]
PartyNotFound(SessionPartyNumber),
#[error("client id `{0}` not found")]
ClientNotFound(ClientId),
}
#[derive(Debug, Default)]
#[cfg(feature = "server")]
pub struct State {
clients: RwLock<HashMap<ClientId, UnboundedSender<String>>>,
groups: RwLock<HashMap<GroupId, Group>>,
}
#[cfg(feature = "server")]
impl State {
pub fn new() -> Self {
Self::default()
}
pub fn new_client_id(&self) -> ClientId {
Uuid::new_v4()
}
pub async fn add_client(&self, id: ClientId, tx: UnboundedSender<String>) {
self.clients.write().await.insert(id, tx);
}
pub async fn get_client(&self, id: &ClientId) -> Option<UnboundedSender<String>> {
self.clients.read().await.get(id).cloned()
}
pub async fn drop_client(&self, id: ClientId) {
let mut groups = self.groups.write().await;
let mut empty_groups: Vec<Uuid> = Vec::new();
groups.iter_mut().for_each(|(group_id, group)| {
group.drop_client(id);
if group.is_empty() {
empty_groups.push(*group_id);
}
});
empty_groups.iter().for_each(|group_id| {
tracing::info!(group_id = group_id.to_string(), "Removing empty group");
groups.remove(group_id);
});
self.clients.write().await.remove(&id);
}
pub async fn add_group(&self, params: Parameters) -> Group {
let uuid = Uuid::new_v4();
let group = Group::new(uuid, params);
let group_c = group.clone();
self.groups.write().await.insert(uuid, group);
group_c
}
pub async fn join_group(&self, group_id: GroupId, client_id: ClientId) -> Result<Group> {
let groups = self.groups.read().await;
let group = groups
.get(&group_id)
.ok_or(StateError::GroupNotFound(group_id))?;
if group.is_full() {
return Err(StateError::GroupIsFull(group_id).into());
}
let mut groups = self.groups.write().await;
let group = groups.get_mut(&group_id).unwrap(); group.add_client(client_id)?;
Ok(group.clone())
}
pub async fn add_session(
&self,
group_id: GroupId,
kind: SessionKind,
value: SessionValue,
) -> Result<(Group, Session)> {
let groups = self.groups.read().await;
groups
.get(&group_id)
.ok_or(StateError::GroupNotFound(group_id))?;
let mut groups = self.groups.write().await;
let group = groups.get_mut(&group_id).unwrap();
let session = group.add_session(kind, value);
Ok((group.clone(), session))
}
pub async fn signup_session(
&self,
client_id: ClientId,
group_id: GroupId,
session_id: SessionId,
) -> Result<(Group, Session, SessionPartyNumber, bool)> {
let groups = self.groups.read().await;
let group = groups
.get(&group_id)
.ok_or(StateError::GroupNotFound(group_id))?;
group
.get_session(&session_id)
.ok_or(StateError::SessionNotFound(session_id, group_id))?;
let mut groups = self.groups.write().await;
let group = groups.get_mut(&group_id).unwrap();
let session = group.get_session_mut(&session_id).unwrap();
let party_index = session.signup(client_id)?;
let parties = session.get_number_of_clients();
let session_c = session.clone();
let threshold = group.params.threshold_reached(session_c.kind, parties);
Ok((group.clone(), session_c, party_index, threshold))
}
pub async fn login_session(
&self,
client_id: ClientId,
group_id: GroupId,
session_id: SessionId,
party_number: SessionPartyNumber,
) -> Result<(Group, Session, bool)> {
let groups = self.groups.read().await;
let group = groups
.get(&group_id)
.ok_or(StateError::GroupNotFound(group_id))?;
group
.get_session(&session_id)
.ok_or(StateError::SessionNotFound(session_id, group_id))?;
let mut groups = self.groups.write().await;
let group = groups.get_mut(&group_id).unwrap();
let session = group.get_session_mut(&session_id).unwrap();
session.login(client_id, party_number)?;
let session_c = session.clone();
let parties = session.party_signups.len();
let threshold = group.params.threshold_reached(session_c.kind, parties);
Ok((group.clone(), session_c, threshold))
}
pub async fn get_client_ids_from_group(&self, group_id: &GroupId) -> Result<Vec<ClientId>> {
let groups = self.groups.read().await;
let group = groups
.get(group_id)
.ok_or(StateError::GroupNotFound(*group_id))?;
let client_ids: Vec<ClientId> = group.clients().iter().copied().collect();
Ok(client_ids)
}
pub async fn get_client_ids_from_session(
&self,
group_id: &GroupId,
session_id: &SessionId,
) -> Result<Vec<ClientId>> {
let groups = self.groups.read().await;
let group = groups
.get(group_id)
.ok_or(StateError::GroupNotFound(*group_id))?;
let session = group
.get_session(session_id)
.ok_or(StateError::SessionNotFound(*session_id, *group_id))?;
let client_ids = session.get_all_client_ids();
Ok(client_ids)
}
pub async fn get_client_id_from_party_number(
&self,
group_id: GroupId,
session_id: SessionId,
party_number: SessionPartyNumber,
) -> Result<ClientId> {
let groups = self.groups.read().await;
let group = groups
.get(&group_id)
.ok_or(StateError::GroupNotFound(group_id))?;
let session = group
.get_session(&session_id)
.ok_or(StateError::SessionNotFound(session_id, group_id))?;
let client_id = session
.get_client_id(party_number)
.ok_or(StateError::PartyNotFound(party_number))?;
Ok(client_id)
}
pub async fn get_party_number_from_client_id(
&self,
group_id: GroupId,
session_id: SessionId,
client_id: ClientId,
) -> Result<SessionPartyNumber> {
let groups = self.groups.read().await;
let group = groups
.get(&group_id)
.ok_or(StateError::GroupNotFound(group_id))?;
let session = group
.get_session(&session_id)
.ok_or(StateError::SessionNotFound(session_id, group_id))?;
let party_number = session
.get_party_number(&client_id)
.ok_or(StateError::ClientNotFound(client_id))?;
Ok(party_number)
}
pub async fn validate_group_and_session(
&self,
group_id: GroupId,
session_id: SessionId,
) -> Result<()> {
let groups = self.groups.read().await;
let group = groups
.get(&group_id)
.ok_or(StateError::GroupNotFound(group_id))?;
group
.get_session(&session_id)
.ok_or(StateError::SessionNotFound(session_id, group_id))?;
Ok(())
}
}