zero_trust_rps/common/rps/
move_validation.rsuse uuid::Uuid;
use crate::common::{message::Round, rps::state::UserMoveMethods as _};
use super::{move_kind::UserMoveKind, state::RpsState};
pub struct UserState {
pub user_id: Uuid,
pub state: RpsState,
}
impl From<&crate::common::message::User> for UserState {
fn from(value: &crate::common::message::User) -> Self {
UserState {
user_id: value.id,
state: value.state.into(),
}
}
}
#[repr(u8)]
#[derive(thiserror::Error, Debug)]
pub enum MoveValidationError {
#[error("A user with that id was already in the room.")]
UserAlreadyInRoom,
#[error("User is not in the current round")]
UserNotInRound,
#[error("User can't do that move right now.")]
UserHasWrongState,
#[error("Other user isn't in the correct state")]
OtherHasWrongState,
}
#[inline]
pub fn validate_move(
user_id: Uuid,
move_kind: impl Into<UserMoveKind>,
users: impl IntoIterator<Item = impl Into<UserState>>,
round: Option<&Round>,
) -> Result<(), MoveValidationError> {
use MoveValidationError::*;
let move_kind: UserMoveKind = move_kind.into();
if matches!(move_kind, UserMoveKind::JoinRoom) {
if users
.into_iter()
.map(Into::<UserState>::into)
.any(|u| u.user_id == user_id)
{
Err(UserAlreadyInRoom)
} else {
Ok(())
}
} else if round.is_some_and(|round| !round.users.contains(&user_id)) {
Err(UserNotInRound) } else {
let allowed_state = move_kind.allowed_state();
let mut users = users.into_iter().map(Into::<UserState>::into);
let others_allowed_states: [_; 2] = [allowed_state, move_kind.resulting_state()];
if let Some(round) = round {
for user in users {
if user.user_id == user_id {
if user.state != allowed_state {
return Err(UserHasWrongState);
}
} else if round.users.contains(&user.user_id) {
if !others_allowed_states.contains(&user.state) {
return Err(OtherHasWrongState);
}
}
}
Ok(())
} else if allowed_state == RpsState::InRoom {
if users.all(|state| state.state == RpsState::InRoom) {
Ok(())
} else {
Err(OtherHasWrongState)
}
} else {
Err(UserHasWrongState)
}
}
}