mpc_manager/state/
session.rs1use super::ClientId;
6use serde::{Deserialize, Serialize};
7use serde_json::Value;
8use std::collections::{HashMap, HashSet};
9use strum::EnumString;
10use thiserror::Error;
11use uuid::Uuid;
12
13pub type SessionValue = Option<Value>;
15pub type SessionId = Uuid;
17pub type SessionPartyNumber = u16;
19
20#[derive(Debug, Error)]
22pub enum SessionError {
23 #[error("party number `{0}` is already occupied by another party")]
24 PartyNumberAlreadyOccupied(SessionPartyNumber),
25 #[error("client `{0}` is already signed up")]
26 ClientAlreadySignedUp(ClientId),
27}
28
29#[derive(Debug, Clone, Copy, Deserialize, Serialize, EnumString)]
31pub enum SessionKind {
32 #[serde(rename = "keygen")]
34 #[strum(serialize = "keygen")]
35 Keygen,
36 #[serde(rename = "sign")]
38 #[strum(serialize = "sign")]
39 Sign,
40}
41
42#[derive(Debug, Deserialize, Serialize)]
44pub struct Session {
45 pub id: SessionId,
47 pub kind: SessionKind,
49 pub value: SessionValue,
55 #[serde(skip)]
57 pub party_signups: HashMap<SessionPartyNumber, ClientId>,
58 #[serde(skip)]
60 pub occupied_party_numbers: Vec<SessionPartyNumber>,
61 #[serde(skip)]
64 pub finished: HashSet<u16>,
65}
66
67impl Session {
68 pub fn new(id: Uuid, kind: SessionKind, value: SessionValue) -> Self {
70 Self {
71 id,
72 kind,
73 value,
74 party_signups: HashMap::new(),
75 occupied_party_numbers: Vec::new(),
76 finished: HashSet::new(),
77 }
78 }
79
80 #[cfg(feature = "server")]
82 pub fn signup(&mut self, client_id: ClientId) -> anyhow::Result<SessionPartyNumber> {
83 if self.is_client_in_session(&client_id) {
84 return Err(SessionError::ClientAlreadySignedUp(client_id).into());
85 }
86 let party_number = self.get_next_party_number();
87 self.add_party(client_id, party_number);
88 Ok(party_number)
89 }
90
91 #[cfg(feature = "server")]
93 pub fn login(
94 &mut self,
95 client_id: ClientId,
96 party_number: SessionPartyNumber,
97 ) -> anyhow::Result<()> {
98 if self.is_client_in_session(&client_id) {
99 return Ok(()); }
101 if self.occupied_party_numbers.contains(&party_number) {
102 return Err(SessionError::PartyNumberAlreadyOccupied(party_number).into());
103 }
104 self.add_party(client_id, party_number);
105 Ok(())
106 }
107
108 #[cfg(feature = "server")]
110 fn add_party(&mut self, client_id: ClientId, party_number: SessionPartyNumber) {
111 self.occupied_party_numbers.push(party_number);
112 self.occupied_party_numbers.sort();
113 self.party_signups.insert(party_number, client_id);
114 }
115
116 #[cfg(feature = "server")]
118 pub fn get_party_number(&self, client_id: &ClientId) -> Option<SessionPartyNumber> {
119 self.party_signups
120 .iter()
121 .find(|(_, id)| id == &client_id)
122 .map(|(party, _)| *party)
123 }
124
125 #[cfg(feature = "server")]
127 pub fn is_client_in_session(&self, client_id: &ClientId) -> bool {
128 self.party_signups.values().any(|id| id == client_id)
129 }
130
131 #[cfg(feature = "server")]
133 pub fn get_client_id(&self, party_number: SessionPartyNumber) -> Option<ClientId> {
134 self.party_signups
135 .iter()
136 .find(|(&pn, _)| pn == party_number)
137 .map(|(_, id)| *id)
138 }
139
140 #[cfg(feature = "server")]
142 pub fn get_all_client_ids(&self) -> Vec<ClientId> {
143 self.party_signups.values().copied().collect()
144 }
145
146 #[cfg(feature = "server")]
148 pub fn get_number_of_clients(&self) -> usize {
149 self.party_signups.len()
150 }
151
152 #[cfg(feature = "server")]
160 fn get_next_party_number(&self) -> SessionPartyNumber {
161 for (i, party) in self.occupied_party_numbers.iter().enumerate() {
162 if (i + 1) != *party as usize {
163 return (i + 1) as SessionPartyNumber;
164 }
165 }
166
167 match self.occupied_party_numbers.last() {
168 Some(party) => party + 1,
169 None => 1,
170 }
171 }
172}
173
174impl Clone for Session {
175 fn clone(&self) -> Self {
179 Self {
180 id: self.id,
181 kind: self.kind,
182 value: self.value.clone(),
183 party_signups: HashMap::new(),
184 occupied_party_numbers: Vec::new(),
185 finished: HashSet::new(),
186 }
187 }
188}