mcsr_ranked_api/user/info/
mod.rs

1pub mod all_seasons;
2
3use chrono::serde::ts_seconds;
4use chrono::{DateTime, Utc};
5use serde::Deserialize;
6
7use super::UserProfile;
8#[cfg(feature = "achievements")]
9use crate::achievement::Achievement;
10use crate::types::{Elo, Phase, PhasePoints, Rank, RankedAndCasual};
11#[cfg(feature = "weekly_races")]
12use crate::weekly_race::result::WeeklyRaceResult;
13
14/// Displayed and total achievements of a user
15#[cfg(feature = "achievements")]
16#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
17#[serde(rename_all = "camelCase")]
18pub struct UserAchievements {
19	display: Box<[Achievement]>,
20	total: Box<[Achievement]>,
21}
22#[cfg(feature = "achievements")]
23impl UserAchievements {
24	/// Achievements the user chose to display on their profile
25	pub fn displayed(&self) -> &[Achievement] {
26		&self.display
27	}
28	/// All of the user's achievements
29	pub fn total(&self) -> &[Achievement] {
30		&self.total
31	}
32}
33
34#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
35#[serde(rename_all = "camelCase")]
36pub struct UserTimestamps {
37	#[serde(with = "ts_seconds")]
38	first_online: DateTime<Utc>,
39	#[serde(with = "ts_seconds")]
40	last_online: DateTime<Utc>,
41	#[serde(with = "ts_seconds")]
42	last_ranked: DateTime<Utc>,
43}
44impl UserTimestamps {
45	pub fn first_online(&self) -> DateTime<Utc> {
46		self.first_online
47	}
48	pub fn last_online(&self) -> DateTime<Utc> {
49		self.last_online
50	}
51	pub fn last_ranked(&self) -> DateTime<Utc> {
52		self.last_ranked
53	}
54}
55
56/// Single statistic in ranked and casual modes
57pub type Stat = RankedAndCasual<Option<u64>>;
58
59/// All of the user's statistics
60#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
61#[serde(rename_all = "camelCase")]
62pub struct UserStats {
63	best_time: Stat,
64	highest_win_streak: Stat,
65	current_win_streak: Stat,
66	played_matches: Stat,
67	playtime: Stat,
68	forfeits: Stat,
69	completions: Stat,
70	wins: Stat,
71	#[serde(rename = "loses")]
72	losses: Stat,
73}
74impl UserStats {
75	pub fn best_time(&self) -> &Stat {
76		&self.best_time
77	}
78
79	pub fn highest_win_streak(&self) -> &Stat {
80		&self.highest_win_streak
81	}
82
83	pub fn current_win_streak(&self) -> &Stat {
84		&self.current_win_streak
85	}
86
87	pub fn played_matches(&self) -> &Stat {
88		&self.played_matches
89	}
90
91	pub fn playtime(&self) -> &Stat {
92		&self.playtime
93	}
94
95	pub fn forfeits(&self) -> &Stat {
96		&self.forfeits
97	}
98
99	pub fn completions(&self) -> &Stat {
100		&self.completions
101	}
102
103	pub fn wins(&self) -> &Stat {
104		&self.wins
105	}
106
107	pub fn losses(&self) -> &Stat {
108		&self.losses
109	}
110}
111
112/// All statistics for season and total
113#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
114#[serde(rename_all = "camelCase")]
115pub struct UserStatistics {
116	season: UserStats,
117	total: UserStats,
118}
119impl UserStatistics {
120	pub fn season(&self) -> &UserStats {
121		&self.season
122	}
123
124	pub fn total(&self) -> &UserStats {
125		&self.total
126	}
127}
128
129/// User's social connection
130#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
131#[serde(rename_all = "camelCase")]
132pub struct UserConnection {
133	id: Box<str>,
134	name: Box<str>,
135}
136impl UserConnection {
137	pub fn id(&self) -> &str {
138		&self.id
139	}
140
141	pub fn name(&self) -> &str {
142		&self.name
143	}
144}
145
146/// All of user's connections
147#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
148#[serde(rename_all = "camelCase")]
149pub struct UserConnections {
150	#[serde(default)]
151	discord: Option<UserConnection>,
152	#[serde(default)]
153	twitch: Option<UserConnection>,
154	#[serde(default)]
155	youtube: Option<UserConnection>,
156}
157impl UserConnections {
158	pub fn discord(&self) -> Option<&UserConnection> {
159		self.discord.as_ref()
160	}
161
162	pub fn twitch(&self) -> Option<&UserConnection> {
163		self.twitch.as_ref()
164	}
165
166	pub fn youtube(&self) -> Option<&UserConnection> {
167		self.youtube.as_ref()
168	}
169}
170
171#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
172#[serde(rename_all = "camelCase")]
173pub struct EloPointsInfo {
174	#[serde(rename = "eloRate")]
175	elo: Option<Elo>,
176	#[serde(rename = "eloRank")]
177	rank: Option<Rank>,
178	#[serde(alias = "phasePoint", alias = "point")]
179	points: PhasePoints,
180}
181impl EloPointsInfo {
182	/// ELO
183	pub fn elo(&self) -> Option<Elo> {
184		self.elo
185	}
186	/// Leaderboard rank
187	pub fn rank(&self) -> Option<Rank> {
188		self.rank
189	}
190	/// Phase points
191	pub fn points(&self) -> PhasePoints {
192		self.points
193	}
194}
195
196#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
197#[serde(rename_all = "camelCase")]
198pub struct PhaseInfo {
199	phase: Phase,
200	#[serde(flatten)]
201	info: EloPointsInfo,
202}
203#[cfg(test)]
204impl PhaseInfo {
205	pub(crate) fn new(phase: Phase, elo: Elo, rank: Rank, points: PhasePoints) -> Self {
206		Self {
207			phase,
208			info: EloPointsInfo {
209				elo: Some(elo),
210				rank: Some(rank),
211				points,
212			},
213		}
214	}
215}
216
217/// Season result
218#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
219#[serde(rename_all = "camelCase")]
220pub struct UserSeasonOutcome {
221	last: EloPointsInfo,
222	highest: Option<Elo>,
223	lowest: Option<Elo>,
224	phases: Box<[PhaseInfo]>,
225}
226#[cfg(test)]
227impl UserSeasonOutcome {
228	pub(crate) fn new(
229		last_elo: Elo,
230		last_rank: Rank,
231		last_points: PhasePoints,
232		highest_elo: Elo,
233		lowest_elo: Elo,
234		phases: impl IntoIterator<Item = PhaseInfo>,
235	) -> Self {
236		Self {
237			last: EloPointsInfo {
238				elo: Some(last_elo),
239				rank: Some(last_rank),
240				points: last_points,
241			},
242			highest: Some(highest_elo),
243			lowest: Some(lowest_elo),
244			phases: phases.into_iter().collect(),
245		}
246	}
247}
248
249impl UserSeasonOutcome {
250	/// Last season's ELO info
251	pub fn last(&self) -> &EloPointsInfo {
252		&self.last
253	}
254	/// Highest ELO
255	pub fn highest(&self) -> Option<&Elo> {
256		self.highest.as_ref()
257	}
258	/// Lowest ELO
259	pub fn lowest(&self) -> Option<&Elo> {
260		self.lowest.as_ref()
261	}
262	/// Phase info
263	pub fn phases(&self) -> &[PhaseInfo] {
264		&self.phases
265	}
266}
267
268/// All of user's available data combined
269#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
270#[serde(rename_all = "camelCase")]
271pub struct UserInfo {
272	#[serde(flatten)]
273	profile: UserProfile,
274	#[cfg(feature = "achievements")]
275	achievements: UserAchievements,
276	timestamp: UserTimestamps,
277	statistics: UserStatistics,
278	connections: UserConnections,
279	season_result: Option<UserSeasonOutcome>,
280	#[cfg(feature = "weekly_races")]
281	weekly_races: Box<[WeeklyRaceResult]>,
282}
283impl UserInfo {
284	/// User's profile
285	pub fn profile(&self) -> &UserProfile {
286		&self.profile
287	}
288	#[cfg(feature = "achievements")]
289	/// User's achievements
290	pub fn achievements(&self) -> &UserAchievements {
291		&self.achievements
292	}
293	/// User's first, last seen and last ranked timestamps
294	pub fn timestamps(&self) -> &UserTimestamps {
295		&self.timestamp
296	}
297	/// User's connections
298	pub fn connections(&self) -> &UserConnections {
299		&self.connections
300	}
301	/// User's elo and phase results
302	pub fn season_result(&self) -> Option<&UserSeasonOutcome> {
303		self.season_result.as_ref()
304	}
305	#[cfg(feature = "weekly_races")]
306	/// User's weekly race stats
307	pub fn weekly_races(&self) -> &[WeeklyRaceResult] {
308		&self.weekly_races
309	}
310}