diff --git a/Cargo.toml b/Cargo.toml index 36afa42..ade5414 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,19 +19,19 @@ path = "src/lib.rs" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -tokio = "1.40.0" -tokio-stream = { version = "0.1.16", default-features = false, features = ["io-util"] } -tokio-util = "0.7.12" -reqwest = { version = "0.12.7", default-features = false, features = ["stream", "rustls-tls"] } -time = "0.3.36" -serde = { version = "1.0.210", features = ["derive"] } -serde_json = "1.0.128" -serde_with = { version = "3.9.0", features = ["time_0_3"] } -futures-util = { version = "0.3.30", default-features = false } +tokio = "1.42.0" +tokio-stream = { version = "0.1.17", default-features = false, features = ["io-util"] } +tokio-util = "0.7.13" +reqwest = { version = "0.12.9", default-features = false, features = ["stream", "rustls-tls"] } +time = "0.3.37" +serde = { version = "1.0.216", features = ["derive"] } +serde_json = "1.0.133" +serde_with = { version = "3.11.0", features = ["time_0_3"] } +futures-util = { version = "0.3.31", default-features = false } comma_serde_urlencoded = "0.8.1" [dev-dependencies] -tokio = { version = "1.40.0", features = ["macros"] } +tokio = { version = "1.42.0", features = ["macros"] } [features] default = ["bot"] diff --git a/README.md b/README.md index 443b235..83488ba 100644 --- a/README.md +++ b/README.md @@ -65,13 +65,13 @@ Below is a list of supported API endpoints as of the last release: | Board | ✅ | | Challenges | ✅ | | Bulk pairings | ❌ | -| Arena tournaments | ❌ | +| Arena tournaments | ❌ | | Swiss tournaments | ❌ | -| Simuls | ❌ | +| Simuls | ✅ | | Studies | ❌ | | Messaging | ✅ | | Broadcasts | ❌ | -| Analysis | ❌ | +| Analysis | ✅ | | External engine | ❌ | | Opening explorer | ✅ | | Tablebase | ✅ | diff --git a/src/api/analysis.rs b/src/api/analysis.rs new file mode 100644 index 0000000..c0aa753 --- /dev/null +++ b/src/api/analysis.rs @@ -0,0 +1,33 @@ +use crate::{ + client::Licheszter, + error::Result, + models::{analysis::CloudAnalysis, game::VariantMode}, +}; + +impl Licheszter { + pub async fn analysis_cloud( + &self, + fen: &str, + multi_pv: Option, + variant: Option, + ) -> Result { + let mut url = self.base_url(); + url.set_path("api/cloud-eval"); + let mut builder = self + .client() + .get(url) + .query(&[("fen", fen.replace(" ", "_"))]); + + // Add the multiPv amount as a query parameter if it's present + if let Some(multi_pv) = multi_pv { + builder = builder.query(&[("multiPv", multi_pv)]); + } + + // Add the variant as a query parameter if it's present + if let Some(variant) = variant { + builder = builder.query(&[("variant", variant)]); + } + + self.into::(builder).await + } +} diff --git a/src/api/mod.rs b/src/api/mod.rs index 6a388f2..3b14cf3 100644 --- a/src/api/mod.rs +++ b/src/api/mod.rs @@ -1,4 +1,5 @@ pub mod account; +pub mod analysis; pub mod challenges; pub mod messaging; pub mod misc; diff --git a/src/api/simuls.rs b/src/api/simuls.rs index a882d70..929e488 100644 --- a/src/api/simuls.rs +++ b/src/api/simuls.rs @@ -1,6 +1,9 @@ use crate::{client::Licheszter, error::Result, models::simul::Simuls}; impl Licheszter { + // Get recently created, started and finished simuls. + // Created and finished simuls are only visible if the host is strong enough. + // When authenticated, the pending simuls will contain your created, but unstarted simuls. pub async fn simuls_current(&self) -> Result { let mut url = self.base_url(); url.set_path("api/simul"); diff --git a/src/models/analysis.rs b/src/models/analysis.rs new file mode 100644 index 0000000..a88c88d --- /dev/null +++ b/src/models/analysis.rs @@ -0,0 +1,17 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)] +#[cfg_attr(feature = "serde-strict", serde(deny_unknown_fields))] +pub struct CloudAnalysis { + pub fen: String, + pub knodes: u32, + pub depth: u8, + pub pvs: Vec, +} + +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)] +#[cfg_attr(feature = "serde-strict", serde(deny_unknown_fields))] +pub struct PositionVariation { + pub moves: String, + pub cp: i8, +} diff --git a/src/models/mod.rs b/src/models/mod.rs index 301d860..779c47f 100644 --- a/src/models/mod.rs +++ b/src/models/mod.rs @@ -1,3 +1,4 @@ +pub mod analysis; pub mod board; pub mod challenge; pub mod chat; diff --git a/tests/analysis.rs b/tests/analysis.rs new file mode 100644 index 0000000..78e61f8 --- /dev/null +++ b/tests/analysis.rs @@ -0,0 +1,55 @@ +use std::{error::Error, sync::LazyLock}; + +use licheszter::{client::Licheszter, models::game::VariantMode}; + +// Connect to test accounts +static LI: LazyLock = LazyLock::new(|| { + Licheszter::builder() + .with_base_url("http://localhost:8080") + .unwrap() + .with_authentication("lip_li") + .build() +}); + +#[tokio::test] +async fn analysis_cloud() { + // Run some test cases + let result = LI + .analysis_cloud( + "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1", + None, + None, + ) + .await; + assert!( + result.is_ok(), + "Failed to get cloud analysis: {:?}", + result.unwrap_err().source().unwrap() + ); + + let result = LI + .analysis_cloud( + "rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq - 0 1", + Some(5), + Some(VariantMode::Standard), + ) + .await; + assert!( + result.is_ok(), + "Failed to get cloud analysis: {:?}", + result.unwrap_err().source().unwrap() + ); + + let result = LI + .analysis_cloud( + "rnb1kbnr/pppp1ppp/8/4p3/5PPq/8/PPPPP2P/RNBQKBNR w KQkq - 1 3", + Some(3), + Some(VariantMode::Atomic), + ) + .await; + assert!( + result.is_err(), + "Getting cloud analysis did not fail: {:?}", + result.unwrap() + ); +}