ugg_types/
matchups.rs

1// Credit to https://github.com/pradishb/ugg-parser for figuring out the
2// structure of the champ overview stats data.
3
4use crate::mappings;
5use serde::Serialize;
6use serde::de::{Deserialize, Deserializer, IgnoredAny, SeqAccess, Visitor};
7use std::collections::HashMap;
8use std::fmt;
9
10pub type Matchups =
11    HashMap<mappings::Region, HashMap<mappings::Rank, HashMap<mappings::Role, WrappedMatchupData>>>;
12
13#[derive(Debug, Clone, Serialize)]
14pub struct WrappedMatchupData {
15    pub data: MatchupData,
16}
17
18impl<'de> Deserialize<'de> for WrappedMatchupData {
19    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
20    where
21        D: Deserializer<'de>,
22    {
23        struct WrappedMatchupDataVisitor;
24
25        impl<'de> Visitor<'de> for WrappedMatchupDataVisitor {
26            type Value = WrappedMatchupData;
27
28            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
29                formatter.write_str("waa")
30            }
31
32            fn visit_seq<V>(self, mut visitor: V) -> Result<WrappedMatchupData, V::Error>
33            where
34                V: SeqAccess<'de>,
35            {
36                match visitor.next_element::<MatchupData>() {
37                    Ok(Some(data)) => {
38                        while let Some(IgnoredAny) = visitor.next_element()? {}
39                        Ok(WrappedMatchupData { data })
40                    }
41                    _ => Err(serde::de::Error::missing_field("top-level element")),
42                }
43            }
44        }
45
46        deserializer.deserialize_seq(WrappedMatchupDataVisitor)
47    }
48}
49
50#[derive(Debug, Clone, Serialize)]
51pub struct MatchupData {
52    pub best_matchups: Vec<Matchup>,
53    pub worst_matchups: Vec<Matchup>,
54    pub total_matches: i32,
55}
56
57#[derive(Debug, Clone, Serialize)]
58pub struct Matchup {
59    pub champion_id: i64,
60    pub wins: i32,
61    pub matches: i32,
62    pub winrate: f64,
63}
64
65impl<'de> Deserialize<'de> for MatchupData {
66    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
67    where
68        D: Deserializer<'de>,
69    {
70        struct MatchupDataVisitor;
71
72        impl<'de> Visitor<'de> for MatchupDataVisitor {
73            type Value = MatchupData;
74
75            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
76                formatter.write_str("overview data")
77            }
78
79            fn visit_seq<V>(self, mut visitor: V) -> Result<MatchupData, V::Error>
80            where
81                V: SeqAccess<'de>,
82            {
83                let mut all_matchups: Vec<Matchup> = vec![];
84                let mut total_matches: i32 = 0;
85
86                while let Ok(data_opt) = visitor.next_element::<InnerData>() {
87                    match data_opt {
88                        Some(data) => {
89                            let wins = data.2 - data.1;
90                            let winrate = f64::from(wins) / f64::from(data.2);
91                            all_matchups.push(Matchup {
92                                champion_id: data.0,
93                                wins,
94                                matches: data.2,
95                                winrate,
96                            });
97                            total_matches += data.2;
98                        }
99                        None => {
100                            break;
101                        }
102                    }
103                }
104
105                // Only consider matchups that represent at least a 0.5% possibility of showing up
106                all_matchups = all_matchups
107                    .into_iter()
108                    .filter(|a| f64::from(a.matches) >= (f64::from(total_matches) / 200.0))
109                    .collect::<Vec<Matchup>>();
110                all_matchups.sort_by(|a, b| b.winrate.partial_cmp(&a.winrate).unwrap());
111
112                if all_matchups.len() >= 5 {
113                    let best_matchups: Vec<Matchup> = all_matchups.clone()[..5].to_vec();
114                    let mut worst_matchups: Vec<Matchup> =
115                        all_matchups.clone()[all_matchups.len() - 5..].to_vec();
116                    worst_matchups.reverse();
117
118                    let matchup_data = MatchupData {
119                        best_matchups,
120                        worst_matchups,
121                        total_matches,
122                    };
123                    Ok(matchup_data)
124                } else {
125                    Ok(MatchupData {
126                        best_matchups: vec![],
127                        worst_matchups: vec![],
128                        total_matches: 0,
129                    })
130                }
131            }
132        }
133
134        deserializer.deserialize_seq(MatchupDataVisitor)
135    }
136}
137
138struct InnerData(i64, i32, i32);
139
140impl<'de> Deserialize<'de> for InnerData {
141    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
142    where
143        D: Deserializer<'de>,
144    {
145        struct InnerSeqVisitor;
146
147        impl<'de> Visitor<'de> for InnerSeqVisitor {
148            type Value = InnerData;
149
150            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
151                formatter.write_str("a sequence with at least 3 elements")
152            }
153
154            fn visit_seq<A>(self, mut visitor: A) -> Result<Self::Value, A::Error>
155            where
156                A: SeqAccess<'de>,
157            {
158                let champion_id = visitor
159                    .next_element()?
160                    .ok_or_else(|| serde::de::Error::invalid_length(0, &self))?;
161
162                let losses = visitor
163                    .next_element()?
164                    .ok_or_else(|| serde::de::Error::invalid_length(1, &self))?;
165
166                let matches = visitor
167                    .next_element()?
168                    .ok_or_else(|| serde::de::Error::invalid_length(2, &self))?;
169
170                while let Some(IgnoredAny) = visitor.next_element()? {}
171
172                Ok(InnerData(champion_id, losses, matches))
173            }
174        }
175
176        deserializer.deserialize_seq(InnerSeqVisitor)
177    }
178}