use reqwest::Method;
use serde::{Deserialize, Serialize};
use crate::client::LichessClient;
use crate::config::Host;
use crate::error::Result;
use crate::http;
use crate::model::LichessTitle;
#[derive(Debug)]
pub struct FideApi<'a> {
client: &'a LichessClient,
}
impl<'a> FideApi<'a> {
pub(crate) fn new(client: &'a LichessClient) -> Self {
Self { client }
}
pub async fn get(&self, player_id: u32) -> Result<LichessFidePlayer> {
let path = format!("/api/fide/player/{player_id}");
let request = self.client.request(Method::GET, Host::Default, &path);
http::json(request, "LichessFidePlayer").await
}
pub async fn ratings(&self, player_id: u32) -> Result<LichessFidePlayerRatings> {
let path = format!("/api/fide/player/{player_id}/ratings");
let request = self.client.request(Method::GET, Host::Default, &path);
http::json(request, "LichessFidePlayerRatings").await
}
pub async fn search(&self, query: &str) -> Result<Vec<LichessFidePlayer>> {
let request = self
.client
.request(Method::GET, Host::Default, "/api/fide/player")
.query(&[("q", query)]);
http::json(request, "Vec<LichessFidePlayer>").await
}
}
impl LichessClient {
#[must_use]
pub fn fide(&self) -> FideApi<'_> {
FideApi::new(self)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum LichessFideGender {
#[serde(rename = "M")]
Male,
#[serde(rename = "F")]
Female,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct LichessFidePhoto {
pub small: String,
pub medium: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub credit: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct LichessFidePlayer {
pub id: u32,
pub name: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub title: Option<LichessTitle>,
pub federation: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub year: Option<u32>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub inactive: Option<u32>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub standard: Option<u32>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub rapid: Option<u32>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub blitz: Option<u32>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub gender: Option<LichessFideGender>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub photo: Option<LichessFidePhoto>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct LichessFidePlayerRatings {
pub standard: Vec<u64>,
pub rapid: Vec<u64>,
pub blitz: Vec<u64>,
}
impl LichessFidePlayerRatings {
#[must_use]
pub fn decode_point(point: u64) -> (u64, u64, u64) {
let rating = point % 10_000;
let date = point / 10_000;
(date / 100, date % 100, rating)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn parses_player_with_null_year() {
let json = r#"{"id":35009192,"name":"Erigaisi Arjun","federation":"IND","year":null}"#;
let player: LichessFidePlayer = serde_json::from_str(json).unwrap();
assert_eq!(player.id, 35_009_192);
assert_eq!(player.year, None);
}
#[test]
fn parses_gender_codes() {
let json = r#"{"id":1,"name":"X","federation":"FRA","gender":"F"}"#;
let player: LichessFidePlayer = serde_json::from_str(json).unwrap();
assert_eq!(player.gender, Some(LichessFideGender::Female));
}
#[test]
fn decodes_a_rating_point() {
assert_eq!(
LichessFidePlayerRatings::decode_point(2_015_081_568),
(2015, 8, 1568)
);
}
}