zero_trust_rps/common/rps/
move_validation.rs1use uuid::Uuid;
2
3use crate::common::{message::Round, rps::state::UserMoveMethods as _};
4
5use super::{move_kind::UserMoveKind, state::RpsState};
6
7pub struct UserState {
8 pub user_id: Uuid,
9 pub state: RpsState,
10}
11
12impl From<&crate::common::message::User> for UserState {
13 fn from(value: &crate::common::message::User) -> Self {
14 UserState {
15 user_id: value.id,
16 state: value.state.into(),
17 }
18 }
19}
20
21#[repr(u8)]
22#[derive(thiserror::Error, Debug)]
23pub enum MoveValidationError {
24 #[error("A user with that id was already in the room.")]
25 UserAlreadyInRoom,
26 #[error("User is not in the current round")]
27 UserNotInRound,
28 #[error("User can't do that move right now.")]
29 UserHasWrongState,
30 #[error("Other user isn't in the correct state")]
31 OtherHasWrongState,
32}
33
34#[inline]
35pub fn validate_move(
36 user_id: Uuid,
37 move_kind: impl Into<UserMoveKind>,
38 users: impl IntoIterator<Item = impl Into<UserState>>,
39 round: Option<&Round>,
40) -> Result<(), MoveValidationError> {
41 use MoveValidationError::*;
42
43 let move_kind: UserMoveKind = move_kind.into();
44
45 if matches!(move_kind, UserMoveKind::JoinRoom) {
46 if users
48 .into_iter()
49 .map(Into::<UserState>::into)
50 .any(|u| u.user_id == user_id)
51 {
52 Err(UserAlreadyInRoom)
53 } else {
54 Ok(())
55 }
56 } else if round.is_some_and(|round| !round.users.contains(&user_id)) {
57 Err(UserNotInRound) } else {
59 let allowed_state = move_kind.allowed_state();
60 let mut users = users.into_iter().map(Into::<UserState>::into);
61 let others_allowed_states: [_; 2] = [allowed_state, move_kind.resulting_state()];
62 if let Some(round) = round {
63 for user in users {
64 if user.user_id == user_id {
65 if user.state != allowed_state {
66 return Err(UserHasWrongState);
67 }
68 } else if round.users.contains(&user.user_id) {
69 if !others_allowed_states.contains(&user.state) {
71 return Err(OtherHasWrongState);
72 }
73 }
74 }
75
76 Ok(())
77 } else if allowed_state == RpsState::InRoom {
78 if users.all(|state| state.state == RpsState::InRoom) {
79 Ok(())
80 } else {
81 Err(OtherHasWrongState)
82 }
83 } else {
84 Err(UserHasWrongState)
85 }
86 }
87}