lichess_api/model/users/
rating_history.rs

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// Lichess returns a hard to use tuple of `[year, month, day, rating]`,
54// because of this Serialize / Deserialize are manually implemented.
55#[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}