1use chrono::DateTime;
2use chrono::{serde::ts_seconds, Utc};
3use serde::Deserialize;
4#[cfg(feature = "serialize")]
5use serde::Serialize;
6use serde_repr::{Deserialize_repr, Serialize_repr};
7use uuid::Uuid;
8
9use crate::types::Time;
10use crate::types::{Elo, EloChange, MatchId, Rank, Season};
11use crate::user::UserProfile;
12#[cfg(feature = "variations")]
13use crate::variations::Variation;
14
15pub mod requests;
16#[cfg(test)]
17mod tests;
18pub mod versus;
19
20#[cfg_attr(feature = "serialize", derive(Serialize))]
21#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize)]
22#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
23pub enum MatchCategory {
24 Any,
25 Custom,
26 High,
27 KillAllBosses,
28 KillWither,
29 KillElderGuardian,
30 AllAdvancements,
31 Half,
32 PoglootQuater,
33 HowDidWeGetHere,
34 HeroOfTheVillage,
35 Arbalistic,
36 CoverMeInDebris,
37 EnterNether,
38 EnterEnd,
39 AllSwords,
40 AllMinerals,
41 #[serde(rename = "FULL_IA_15_LVL")]
42 FullIa15Lvl,
43 AllWorkstations,
44 FullInv,
45 StackOfLimeWool,
46 AllPortals,
47 AllBlocks,
48 MineAChunk,
49}
50
51#[cfg_attr(feature = "serialize", derive(Serialize))]
52#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
53pub struct MatchSeedInfo {
54 pub id: Option<Box<str>>,
56 pub overworld: Option<OverworldType>,
57 #[serde(rename = "nether")]
58 pub bastion: Option<BastionType>,
59 #[serde(default)]
61 pub end_towers: Option<[u8; 4]>,
62 #[serde(default)]
63 #[cfg(feature = "variations")]
64 pub variations: Box<[Variation]>,
65 #[cfg(not(feature = "variations"))]
66 pub variations: Box<[Box<str>]>,
67}
68impl MatchSeedInfo {
69 pub fn id(&self) -> Option<&str> {
71 self.id.as_ref().map(AsRef::as_ref)
72 }
73 #[cfg(not(feature = "variations"))]
74 pub fn variations(&self) -> &[Box<str>] {
75 self.variations.as_ref()
76 }
77 #[cfg(feature = "variations")]
78 pub fn variations(&self) -> &[Variation] {
79 self.variations.as_ref()
80 }
81}
82
83#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize_repr, Serialize_repr)]
84#[repr(u8)]
85pub enum MatchType {
86 Causal = 1,
87 Ranked = 2,
88 Private = 3,
89 Event = 4,
90}
91
92#[cfg_attr(feature = "serialize", derive(Serialize))]
93#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
94#[serde(rename_all = "camelCase")]
95pub struct MatchOutcome {
96 #[serde(rename = "uuid")]
97 pub winner_uuid: Option<Uuid>,
98 pub time: Time,
99}
100
101#[cfg_attr(feature = "serialize", derive(Serialize))]
103#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
104#[serde(rename_all = "camelCase")]
105pub struct MatchRank {
106 pub season: Option<Rank>,
107 pub all_time: Option<Rank>,
108}
109
110#[cfg_attr(feature = "serialize", derive(Serialize))]
112#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
113#[serde(rename_all = "camelCase")]
114pub struct MatchEloUpdate {
115 #[serde(rename = "uuid")]
116 pub player_uuid: Uuid,
117 pub change: Option<EloChange>,
118 #[serde(rename = "eloRate")]
119 pub elo: Option<Elo>,
120}
121
122#[cfg_attr(feature = "serialize", derive(Serialize))]
124#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize)]
125#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
126pub enum OverworldType {
127 Village,
128 BuriedTreasure,
129 Shipwreck,
130 RuinedPortal,
131 DesertTemple,
132}
133
134#[cfg_attr(feature = "serialize", derive(Serialize))]
136#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize)]
137#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
138pub enum BastionType {
139 Housing,
140 Treasure,
141 Bridge,
142 Stables,
143}
144
145#[cfg_attr(feature = "serialize", derive(Serialize))]
147#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
148#[serde(rename_all = "camelCase")]
149pub struct MatchCompletion {
150 #[serde(rename = "uuid")]
151 pub player_uuid: Uuid,
152 pub time: Time,
153}
154
155#[cfg_attr(feature = "serialize", derive(Serialize))]
157#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
158#[serde(rename_all = "camelCase")]
159pub struct MatchTimelineEvent {
160 #[serde(rename = "uuid")]
161 pub player_uuid: Uuid,
162 pub time: Time,
163 #[serde(rename = "type")]
164 pub id: Box<str>,
165}
166impl MatchTimelineEvent {
167 pub fn id(&self) -> &str {
168 &self.id
169 }
170}
171
172#[cfg_attr(feature = "serialize", derive(Serialize))]
174#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
175#[serde(rename_all = "camelCase")]
176pub struct MatchInfo {
177 pub id: MatchId,
178 #[serde(rename = "type")]
179 pub kind: MatchType,
180 pub season: Season,
181 pub category: Option<MatchCategory>,
182 #[serde(with = "ts_seconds")]
183 pub date: DateTime<Utc>,
184 pub players: Box<[UserProfile]>,
185 pub spectators: Box<[UserProfile]>,
186 pub seed: Option<MatchSeedInfo>,
187 pub result: MatchOutcome,
188 pub forfeited: bool,
189 pub decayed: bool,
190 pub rank: MatchRank,
191 #[serde(rename = "changes")]
192 pub elo_updates: Box<[MatchEloUpdate]>,
193}
194impl MatchInfo {
195 pub fn players(&self) -> &[UserProfile] {
197 &self.players
198 }
199 pub fn spectators(&self) -> &[UserProfile] {
201 &self.spectators
202 }
203 pub fn elo_updates(&self) -> &[MatchEloUpdate] {
205 &self.elo_updates
206 }
207 pub fn overworld_type(&self) -> Option<OverworldType> {
209 self.seed.as_ref().and_then(|s| s.overworld)
210 }
211 pub fn bastion_type(&self) -> Option<BastionType> {
213 self.seed.as_ref().and_then(|s| s.bastion)
214 }
215}
216
217#[cfg_attr(feature = "serialize", derive(Serialize))]
219#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
220#[serde(rename_all = "camelCase")]
221pub struct AdvancedMatchInfo {
222 #[serde(flatten)]
224 info: MatchInfo,
225 completions: Box<[MatchCompletion]>,
227 #[serde(rename = "timelines")]
228 timeline_events: Box<[MatchTimelineEvent]>,
230 #[serde(rename = "replayExist")]
231 replay_exists: bool,
232}
233impl AdvancedMatchInfo {
234 pub fn completions(&self) -> &[MatchCompletion] {
236 &self.completions
237 }
238 pub fn timeline_events(&self) -> &[MatchTimelineEvent] {
240 &self.timeline_events
241 }
242}