1use serde::{
2 Deserialize, Deserializer, Serialize, Serializer,
3 ser::{SerializeSeq, SerializeTuple},
4};
5
6#[derive(Default, Clone, Debug, Serialize)]
7pub struct GetQuery {
8 pub trophies: bool,
9}
10
11pub type GetRequest = crate::model::Request<GetQuery>;
12
13impl GetRequest {
14 pub fn new(username: &str) -> Self {
15 Self::get(format!("/api/user/{username}/rating-history"), None, None)
16 }
17}
18
19impl<S: AsRef<str>> From<S> for GetRequest {
20 fn from(s: S) -> Self {
21 Self::new(s.as_ref())
22 }
23}
24
25#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
26pub enum RatingType {
27 Bullet,
28 Blitz,
29 Rapid,
30 Classical,
31 Correspondence,
32 Chess960,
33 #[serde(rename = "King of the Hill")]
34 KingOfTheHill,
35 #[serde(rename = "Three-check")]
36 ThreeCheck,
37 Antichess,
38 Atomic,
39 Horde,
40 #[serde(rename = "Racing Kings")]
41 RacingKings,
42 Crazyhouse,
43 UltraBullet,
44 Puzzles,
45}
46
47#[derive(Clone, Debug, Deserialize, Serialize)]
48pub struct RatingEntry {
49 pub name: RatingType,
50 pub points: Vec<RatingPoint>,
51}
52
53#[derive(Clone, Debug, Deserialize)]
56pub struct RatingPoint {
57 pub year: u32,
58 pub month: u32,
59 pub day: u32,
60 pub rating: u32,
61}
62
63impl Serialize for RatingPoint {
64 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
65 where
66 S: Serializer,
67 {
68 let mut seq = serializer.serialize_tuple(4)?;
69 seq.serialize_element(&self.year)?;
70 seq.serialize_element(&self.month)?;
71 seq.serialize_element(&self.day)?;
72 seq.serialize_element(&self.rating)?;
73 seq.end()
74 }
75}
76
77#[derive(Default, Clone, Debug)]
78pub struct RatingHistory {
79 pub bullet: Option<Box<[RatingPoint]>>,
80 pub blitz: Option<Box<[RatingPoint]>>,
81 pub rapid: Option<Box<[RatingPoint]>>,
82 pub classical: Option<Box<[RatingPoint]>>,
83 pub correspondence: Option<Box<[RatingPoint]>>,
84 pub chess960: Option<Box<[RatingPoint]>>,
85 pub king_of_the_hill: Option<Box<[RatingPoint]>>,
86 pub three_check: Option<Box<[RatingPoint]>>,
87 pub antichess: Option<Box<[RatingPoint]>>,
88 pub atomic: Option<Box<[RatingPoint]>>,
89 pub horde: Option<Box<[RatingPoint]>>,
90 pub racing_kings: Option<Box<[RatingPoint]>>,
91 pub crazyhouse: Option<Box<[RatingPoint]>>,
92 pub puzzles: Option<Box<[RatingPoint]>>,
93 pub ultra_bullet: Option<Box<[RatingPoint]>>,
94}
95
96macro_rules! index {
97 ($(($field: ident, $type: ident)),* $(,)?) => {
98 impl RatingHistory {
99 fn get(&self, index: RatingType) -> Option<&[RatingPoint]> {
100 match index {
101 $(RatingType::$type => &self.$field),*
102 }.as_ref().map(|points|points.as_ref())
103
104 }
105
106 fn get_mut(&mut self, index: RatingType) -> &mut Option<Box<[RatingPoint]>> {
107 match index {
108 $(RatingType::$type => &mut self.$field),*
109 }
110 }
111 }
112
113 const RATING_TYPES: [RatingType; 15] = [$(RatingType::$type),*];
114 };
115}
116
117index!(
118 (bullet, Bullet),
119 (blitz, Blitz),
120 (rapid, Rapid),
121 (classical, Classical),
122 (correspondence, Correspondence),
123 (chess960, Chess960),
124 (king_of_the_hill, KingOfTheHill),
125 (three_check, ThreeCheck),
126 (antichess, Antichess),
127 (atomic, Atomic),
128 (horde, Horde),
129 (racing_kings, RacingKings),
130 (crazyhouse, Crazyhouse),
131 (puzzles, Puzzles),
132 (ultra_bullet, UltraBullet),
133);
134
135impl Serialize for RatingHistory {
136 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
137 where
138 S: Serializer,
139 {
140 #[derive(Serialize)]
141 struct RefRatingEntry<'a> {
142 name: RatingType,
143 points: &'a [RatingPoint],
144 }
145
146 let mut seq = serializer.serialize_seq(None)?;
147
148 for name in RATING_TYPES {
149 if let Some(points) = &self.get(name) {
150 seq.serialize_element(&RefRatingEntry { name, points })?;
151 }
152 }
153
154 seq.end()
155 }
156}
157
158impl<'de> Deserialize<'de> for RatingHistory {
159 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
160 where
161 D: Deserializer<'de>,
162 {
163 let points = <Vec<RatingEntry>>::deserialize(deserializer)?;
164 let mut history = Self::default();
165
166 for point in points {
167 *history.get_mut(point.name) = Some(point.points.into_boxed_slice());
168 }
169
170 Ok(history)
171 }
172}