Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions lib/src/api/board.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,13 @@ impl LichessApi<reqwest::Client> {
self.get_ok(request.into()).await
}

pub async fn board_claim_draw(
&self,
request: impl Into<claim_draw::PostRequest>,
) -> Result<bool> {
self.get_ok(request.into()).await
}

pub async fn board_claim_victory(
&self,
request: impl Into<claim_victory::PostRequest>,
Expand Down
21 changes: 21 additions & 0 deletions lib/src/api/bot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,20 @@ impl LichessApi<reqwest::Client> {
self.get_ok(request.into()).await
}

pub async fn bot_claim_draw(
&self,
request: impl Into<claim_draw::PostRequest>,
) -> Result<bool> {
self.get_ok(request.into()).await
}

pub async fn bot_claim_victory(
&self,
request: impl Into<claim_victory::PostRequest>,
) -> Result<bool> {
self.get_ok(request.into()).await
}

pub async fn bot_draw_game(&self, request: impl Into<draw::PostRequest>) -> Result<bool> {
self.get_ok(request.into()).await
}
Expand Down Expand Up @@ -54,6 +68,13 @@ impl LichessApi<reqwest::Client> {
self.get_streamed_models(request.into()).await
}

pub async fn bot_handle_takeback(
&self,
request: impl Into<takeback::PostRequest>,
) -> Result<bool> {
self.get_ok(request.into()).await
}

pub async fn bot_upgrade_account(
&self,
request: impl Into<upgrade::PostRequest>,
Expand Down
14 changes: 14 additions & 0 deletions lib/src/api/challenges.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,4 +54,18 @@ impl LichessApi<reqwest::Client> {
) -> Result<bool> {
self.get_ok(request.into()).await
}

pub async fn show_challenge(
&self,
request: impl Into<show::GetRequest>,
) -> Result<ChallengeJson> {
self.get_single_model(request.into()).await
}

pub async fn admin_challenge_tokens(
&self,
request: impl Into<admin_challenge::PostRequest>,
) -> Result<AdminChallengeTokenResults> {
self.get_single_model(request.into()).await
}
}
7 changes: 7 additions & 0 deletions lib/src/api/fide.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,11 @@ impl LichessApi<reqwest::Client> {
pub async fn get_fide_player(&self, request: impl Into<player::GetRequest>) -> Result<Player> {
self.get_single_model(request.into()).await
}

pub async fn get_fide_player_ratings(
&self,
request: impl Into<ratings::GetRequest>,
) -> Result<ratings::PlayerRatings> {
self.get_single_model(request.into()).await
}
}
22 changes: 22 additions & 0 deletions lib/src/api/games.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,4 +74,26 @@ impl LichessApi<reqwest::Client> {
) -> Result<import::ImportData> {
self.get_single_model(request.into()).await
}

pub async fn export_bookmarked_games(
&self,
request: impl Into<export::bookmarks::GetRequest>,
) -> Result<impl StreamExt<Item = Result<GameJson>>> {
self.get_streamed_models(request.into()).await
}

pub async fn export_imported_games(&self) -> Result<impl StreamExt<Item = Result<String>>> {
self.get_pgn(export::imports::GetRequest::new()).await
}

pub async fn get_game_chat(
&self,
request: impl Into<chat::GetRequest>,
) -> Result<impl StreamExt<Item = Result<chat::ChatLine>>> {
self.get_streamed_models(request.into()).await
}

pub async fn bookmark_game(&self, request: impl Into<bookmark::PostRequest>) -> Result<()> {
self.get_empty(request.into()).await
}
}
21 changes: 21 additions & 0 deletions lib/src/api/puzzles.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,4 +54,25 @@ impl LichessApi<reqwest::Client> {
) -> Result<race::Race> {
self.get_single_model(request.into()).await
}

pub async fn get_puzzle_batch(
&self,
request: impl Into<batch::GetRequest>,
) -> Result<batch::Select> {
self.get_single_model(request.into()).await
}

pub async fn solve_puzzle_batch(
&self,
request: impl Into<batch::PostRequest>,
) -> Result<batch::SolveResponse> {
self.get_single_model(request.into()).await
}

pub async fn get_puzzle_race_results(
&self,
request: impl Into<racer::GetRequest>,
) -> Result<racer::RaceResults> {
self.get_single_model(request.into()).await
}
}
20 changes: 20 additions & 0 deletions lib/src/model/board/claim_draw.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
use crate::model::Request;
use serde::Serialize;

#[derive(Default, Clone, Debug, Serialize)]
pub struct PostQuery;

pub type PostRequest = Request<PostQuery>;

impl PostRequest {
pub fn new(game_id: &str) -> Self {
let path = format!("/api/board/game/{game_id}/claim-draw");
Self::post(path, None, None, None)
}
}

impl<S: AsRef<str>> From<S> for PostRequest {
fn from(s: S) -> Self {
Self::new(s.as_ref())
}
}
1 change: 1 addition & 0 deletions lib/src/model/board/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ pub mod stream;
pub mod abort;
pub mod berserk;
pub mod chat;
pub mod claim_draw;
pub mod claim_victory;
pub mod draw;
pub mod r#move;
Expand Down
20 changes: 20 additions & 0 deletions lib/src/model/bot/claim_draw.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
use crate::model::Request;
use serde::Serialize;

#[derive(Default, Clone, Debug, Serialize)]
pub struct PostQuery;

pub type PostRequest = Request<PostQuery>;

impl PostRequest {
pub fn new(game_id: &str) -> Self {
let path = format!("/api/bot/game/{game_id}/claim-draw");
Self::post(path, None, None, None)
}
}

impl<S: AsRef<str>> From<S> for PostRequest {
fn from(s: S) -> Self {
Self::new(s.as_ref())
}
}
20 changes: 20 additions & 0 deletions lib/src/model/bot/claim_victory.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
use crate::model::Request;
use serde::Serialize;

#[derive(Default, Clone, Debug, Serialize)]
pub struct PostQuery;

pub type PostRequest = Request<PostQuery>;

impl PostRequest {
pub fn new(game_id: &str) -> Self {
let path = format!("/api/bot/game/{game_id}/claim-victory");
Self::post(path, None, None, None)
}
}

impl<S: AsRef<str>> From<S> for PostRequest {
fn from(s: S) -> Self {
Self::new(s.as_ref())
}
}
3 changes: 3 additions & 0 deletions lib/src/model/bot/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@ pub mod stream;

pub mod abort;
pub mod chat;
pub mod claim_draw;
pub mod claim_victory;
pub mod draw;
pub mod r#move;
pub mod online;
pub mod resign;
pub mod takeback;
pub mod upgrade;
15 changes: 15 additions & 0 deletions lib/src/model/bot/takeback.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
use crate::model::Request;
use serde::Serialize;

#[derive(Default, Clone, Debug, Serialize)]
pub struct PostQuery;

pub type PostRequest = Request<PostQuery>;

impl PostRequest {
pub fn new(game_id: &str, accept: bool) -> Self {
let accept = if accept { "yes" } else { "no" };
let path = format!("/api/bot/game/{game_id}/takeback/{accept}");
Self::post(path, None, None, None)
}
}
87 changes: 87 additions & 0 deletions lib/src/model/broadcasts/create_tournament.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use crate::model::broadcasts::BroadcastTiebreakExtendedCode;
use crate::model::{Body, Request};
use serde::Serialize;
use serde::ser::SerializeMap;
use serde_with::skip_serializing_none;

#[skip_serializing_none]
Expand Down Expand Up @@ -37,6 +38,48 @@ pub struct CreateBroadcastTournamentForm {
pub teams: Option<String>,
pub tier: Option<i32>,
pub tiebreaks: Option<Vec<BroadcastTiebreakExtendedCode>>,
#[serde(flatten)]
pub grouping: Option<BroadcastGrouping>,
}

/// Groups this broadcast tournament together with others.
///
/// `score_groups` serializes as indexed form keys (`grouping.scoreGroups[0]`,
/// `grouping.scoreGroups[1]`, ...) per the Lichess API's non-standard array
/// encoding for this field, which a single struct field cannot otherwise
/// produce with `serde_urlencoded` - hence the manual `Serialize` impl below.
#[derive(Default, Clone, Debug)]
pub struct BroadcastGrouping {
pub info_name: Option<String>,
/// Linebreak separated list of tournament IDs to group together.
pub info_tours: Option<String>,
/// Each entry is a comma separated list of tournament IDs grouped for scoring.
pub score_groups: Option<Vec<String>>,
}

impl Serialize for BroadcastGrouping {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
let score_groups_len = self.score_groups.as_ref().map_or(0, Vec::len);
let len = self.info_name.is_some() as usize
+ self.info_tours.is_some() as usize
+ score_groups_len;
let mut map = serializer.serialize_map(Some(len))?;
if let Some(name) = &self.info_name {
map.serialize_entry("grouping.info.name", name)?;
}
if let Some(tours) = &self.info_tours {
map.serialize_entry("grouping.info.tours", tours)?;
}
if let Some(score_groups) = &self.score_groups {
for (i, group) in score_groups.iter().enumerate() {
map.serialize_entry(&format!("grouping.scoreGroups[{i}]"), group)?;
}
}
map.end()
}
}

#[derive(Default, Clone, Debug, Serialize)]
Expand All @@ -49,3 +92,47 @@ impl PostRequest {
Self::post("/broadcast/new", None, Body::Form(form), None)
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn grouping_serializes_score_groups_as_indexed_keys() {
let form = CreateBroadcastTournamentForm {
name: "Sinquefield Cup".to_string(),
grouping: Some(BroadcastGrouping {
info_name: Some("Chess Olympiad | Open".to_string()),
info_tours: Some("wYigbpXq\nM5YHvpOX".to_string()),
score_groups: Some(vec![
"wYigbpXq,M5YHvpOX".to_string(),
"q6ezoCXP".to_string(),
]),
}),
..Default::default()
};

let encoded = serde_urlencoded::to_string(&form).unwrap();

assert_eq!(
encoded,
"name=Sinquefield+Cup\
&grouping.info.name=Chess+Olympiad+%7C+Open\
&grouping.info.tours=wYigbpXq%0AM5YHvpOX\
&grouping.scoreGroups%5B0%5D=wYigbpXq%2CM5YHvpOX\
&grouping.scoreGroups%5B1%5D=q6ezoCXP"
);
}

#[test]
fn grouping_omitted_when_none() {
let form = CreateBroadcastTournamentForm {
name: "Sinquefield Cup".to_string(),
..Default::default()
};

let encoded = serde_urlencoded::to_string(&form).unwrap();

assert_eq!(encoded, "name=Sinquefield+Cup");
}
}
28 changes: 28 additions & 0 deletions lib/src/model/challenges/admin_challenge.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
use crate::model::{Body, Request};
use serde::Serialize;

#[derive(Default, Clone, Debug, Serialize)]
pub struct PostQuery;

pub type PostRequest = Request<PostQuery, AdminChallengeTokens>;

impl PostRequest {
pub fn new(tokens: AdminChallengeTokens) -> Self {
let path = "/api/token/admin-challenge".to_string();
Self::post(path, None, Body::Form(tokens), None)
}
}

#[derive(Clone, Debug, Serialize)]
pub struct AdminChallengeTokens {
/// Usernames separated with commas
pub users: String,
/// User visible description of the token
pub description: String,
}

impl From<AdminChallengeTokens> for PostRequest {
fn from(tokens: AdminChallengeTokens) -> Self {
Self::new(tokens)
}
}
5 changes: 5 additions & 0 deletions lib/src/model/challenges/mod.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,21 @@
pub mod accept;
pub mod add_time;
pub mod admin_challenge;
pub mod ai;
pub mod cancel;
pub mod create;
pub mod decline;
pub mod list;
pub mod open;
pub mod show;
pub mod start_clocks;

use crate::model::{Color, Days, GameCompat, Speed, Title, Variant, VariantKey};
use serde::{Deserialize, Serialize};
use serde_with::skip_serializing_none;
use std::collections::HashMap;

pub type AdminChallengeTokenResults = HashMap<String, String>;

#[derive(Clone, Debug, Serialize)]
pub struct OpenChallenge {
Expand Down
Loading
Loading