1use std::fmt;
2
3use serde::Serialize;
4use serde::de::{Deserialize, Deserializer, IgnoredAny, SeqAccess, Visitor};
5
6use crate::default_overview::{Abilities, Items, LateItem};
7use crate::overview::handle_unknown;
8
9#[derive(Debug, Clone, Serialize)]
10pub struct ArenaOverviewData {
11 pub starting_items: Items,
12 pub core_items: Items,
13 pub abilities: Abilities,
14 pub item_4_options: Vec<LateItem>,
15 pub item_5_options: Vec<LateItem>,
16 pub item_6_options: Vec<LateItem>,
17 pub consumables: Vec<LateItem>,
18 pub prismatic_items: Vec<PrismaticItem>,
19 pub wins: i64,
20 pub matches: i64,
21 pub low_sample_size: bool,
22 pub champion_synergies: Vec<ChampionSynergy>,
23 pub augments: Vec<Augment>,
24}
25
26#[derive(Debug, Clone, Serialize, Eq, PartialEq)]
27pub struct PrismaticItem {
28 pub id: i64,
29 pub matches: i64,
30 pub wins: i64,
31}
32
33impl PrismaticItem {
34 #[must_use]
35 #[allow(clippy::cast_precision_loss)]
36 pub fn winrate(&self) -> f64 {
37 if self.matches <= 0 {
38 return 0f64;
39 }
40 (self.wins as f64) / (self.matches as f64)
41 }
42}
43
44impl PartialOrd for PrismaticItem {
45 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
46 Some(self.cmp(other))
47 }
48}
49
50impl Ord for PrismaticItem {
51 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
52 self.winrate().total_cmp(&other.winrate())
53 }
54}
55
56impl<'de> Deserialize<'de> for PrismaticItem {
57 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
58 where
59 D: Deserializer<'de>,
60 {
61 struct PrismaticItemVisitor;
62
63 impl<'de> Visitor<'de> for PrismaticItemVisitor {
64 type Value = PrismaticItem;
65
66 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
67 formatter.write_str("prismatic items")
68 }
69
70 fn visit_seq<V>(self, mut visitor: V) -> Result<PrismaticItem, V::Error>
71 where
72 V: SeqAccess<'de>,
73 {
74 let id = handle_unknown(visitor.next_element::<i64>());
75
76 let _ = visitor.next_element::<IgnoredAny>().is_ok();
78 let _ = visitor.next_element::<IgnoredAny>().is_ok();
79
80 let wins = handle_unknown(visitor.next_element::<i64>());
81 let matches = handle_unknown(visitor.next_element::<i64>());
82 Ok(PrismaticItem { id, matches, wins })
83 }
84 }
85
86 deserializer.deserialize_seq(PrismaticItemVisitor)
87 }
88}
89
90#[derive(Debug, Clone, Serialize, Eq, PartialEq)]
91pub struct ChampionSynergy {
92 pub id: i64,
93 top_four: i64,
94 picked: i64,
95 first: i64,
96 sum_of_placements: i64,
97}
98
99impl ChampionSynergy {
100 #[must_use]
101 #[allow(clippy::cast_precision_loss)]
102 pub fn top_four_rate(&self) -> f64 {
103 if self.picked <= 0 {
104 return 0f64;
105 }
106 (self.top_four as f64) / (self.picked as f64)
107 }
108}
109
110impl PartialOrd for ChampionSynergy {
111 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
112 Some(self.cmp(other))
113 }
114}
115
116impl Ord for ChampionSynergy {
117 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
118 self.top_four_rate().total_cmp(&other.top_four_rate())
119 }
120}
121
122impl<'de> Deserialize<'de> for ChampionSynergy {
123 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
124 where
125 D: Deserializer<'de>,
126 {
127 struct ChampionSynergiesVisitor;
128
129 impl<'de> Visitor<'de> for ChampionSynergiesVisitor {
130 type Value = ChampionSynergy;
131
132 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
133 formatter.write_str("champion synergy")
134 }
135
136 fn visit_seq<V>(self, mut visitor: V) -> Result<ChampionSynergy, V::Error>
137 where
138 V: SeqAccess<'de>,
139 {
140 let id = handle_unknown(visitor.next_element::<i64>());
141 let top_four = handle_unknown(visitor.next_element::<i64>());
142 let picked = handle_unknown(visitor.next_element::<i64>());
143 let first = handle_unknown(visitor.next_element::<i64>());
144 let sum_of_placements = handle_unknown(visitor.next_element::<i64>());
145
146 Ok(ChampionSynergy {
147 id,
148 top_four,
149 picked,
150 first,
151 sum_of_placements,
152 })
153 }
154 }
155
156 deserializer.deserialize_seq(ChampionSynergiesVisitor)
157 }
158}
159
160#[derive(Debug, Clone, Serialize, PartialEq, Eq)]
161pub struct Augment {
162 pub id: i64,
163 wins: i64,
164 matches: i64,
165}
166
167impl Augment {
168 #[must_use]
169 #[allow(clippy::cast_precision_loss)]
170 pub fn winrate(&self) -> f64 {
171 if self.matches <= 0 {
172 return 0f64;
173 }
174 (self.wins as f64) / (self.matches as f64)
175 }
176}
177
178impl PartialOrd for Augment {
179 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
180 Some(self.cmp(other))
181 }
182}
183
184impl Ord for Augment {
185 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
186 self.winrate().total_cmp(&other.winrate())
187 }
188}
189
190impl<'de> Deserialize<'de> for Augment {
191 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
192 where
193 D: Deserializer<'de>,
194 {
195 struct AugmentVisitor;
196
197 impl<'de> Visitor<'de> for AugmentVisitor {
198 type Value = Augment;
199
200 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
201 formatter.write_str("augment")
202 }
203
204 fn visit_seq<V>(self, mut visitor: V) -> Result<Augment, V::Error>
205 where
206 V: SeqAccess<'de>,
207 {
208 let id = handle_unknown(visitor.next_element::<i64>());
209 let wins = handle_unknown(visitor.next_element::<i64>());
210 let matches = handle_unknown(visitor.next_element::<i64>());
211
212 Ok(Augment { id, wins, matches })
213 }
214 }
215
216 deserializer.deserialize_seq(AugmentVisitor)
217 }
218}
219
220impl<'de> Deserialize<'de> for ArenaOverviewData {
221 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
222 where
223 D: Deserializer<'de>,
224 {
225 struct ArenaOverviewDataVisitor;
226
227 impl<'de> Visitor<'de> for ArenaOverviewDataVisitor {
228 type Value = ArenaOverviewData;
229
230 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
231 formatter.write_str("overview data")
232 }
233
234 fn visit_seq<V>(self, mut visitor: V) -> Result<ArenaOverviewData, V::Error>
235 where
236 V: SeqAccess<'de>,
237 {
238 let _ = visitor.next_element::<IgnoredAny>().is_ok();
240
241 let _ = visitor.next_element::<IgnoredAny>().is_ok();
243
244 let starting_items = visitor
245 .next_element::<Items>()?
246 .ok_or(serde::de::Error::custom("Could not parse starting items."))?;
247 let core_items = visitor
248 .next_element::<Items>()?
249 .ok_or(serde::de::Error::custom("Could not parse core items."))?;
250 let abilities = visitor
251 .next_element::<Abilities>()?
252 .ok_or(serde::de::Error::custom("Could not parse abilities."))?;
253
254 let late_items = visitor
255 .next_element::<(
256 Vec<LateItem>,
257 Vec<LateItem>,
258 Vec<LateItem>,
259 Vec<LateItem>,
260 Vec<PrismaticItem>,
261 IgnoredAny,
263 )>()?
264 .ok_or(serde::de::Error::custom(
265 "Could not parse late / prismatic items.",
266 ))?;
267
268 let mut prismatic_items = late_items.4;
271 prismatic_items.sort_by(|a, b| b.cmp(a));
272
273 let match_info = visitor.next_element::<(i64, i64)>()?.unwrap_or_default();
274 let low_sample_size = match_info.1 < 1000;
275
276 let _ = visitor.next_element::<IgnoredAny>().is_ok();
278
279 let _ = visitor.next_element::<IgnoredAny>().is_ok();
281
282 let _ = visitor.next_element::<IgnoredAny>().is_ok();
284
285 let mut champion_synergies = visitor
288 .next_element::<Vec<ChampionSynergy>>()?
289 .unwrap_or_default();
290 champion_synergies.sort_by(|a, b| b.cmp(a));
291
292 let mut augments = visitor.next_element::<Vec<Augment>>()?.unwrap_or_default();
297 augments.sort_by(|a, b| b.cmp(a));
298
299 while let Some(IgnoredAny) = visitor.next_element()? {}
301
302 let arena_overview_data = ArenaOverviewData {
303 starting_items,
304 core_items,
305 abilities,
306 item_4_options: late_items.0,
307 item_5_options: late_items.1,
308 item_6_options: late_items.2,
309 consumables: late_items.3,
310 prismatic_items,
311 wins: match_info.0,
312 matches: match_info.1,
313 low_sample_size,
314 champion_synergies,
315 augments,
316 };
317 Ok(arena_overview_data)
318 }
319 }
320
321 deserializer.deserialize_seq(ArenaOverviewDataVisitor)
322 }
323}