diff --git a/.gitignore b/.gitignore index 26bebc5..3ff400d 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,6 @@ target/ Cargo.lock TODO.md -merge-dev.sh .vscode/ check_examples.sh +tetr_ch_bin/ diff --git a/CHANGELOG.md b/CHANGELOG.md index ddd7bcd..e966221 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,30 @@ +# v0.5.0 2023-11-30 + +## Fixes + +- Decoding error caused by the TWC badges [[#4](https://github.com/Rinrin0413/tetr-ch-rs/issues/4)] +- Missing property `currentbtbchainpower` in struct `SinglePlayEndCtx` [[#6](https://github.com/Rinrin0413/tetr-ch-rs/issues/6)] +- Some outdated example code (01, 04) +- Some typos in document and `CHANGELOG.md` + +## Additions + +- Support new type of line clear "Pentas" and "T-Spin Pentas" [[#7](https://github.com/Rinrin0413/tetr-ch-rs/issues/7)] +- A field `group` in struct `Badge` [[#5](https://github.com/Rinrin0413/tetr-ch-rs/issues/5)] + +## Changes + +- Rename a field `attack` to `attacks` of struct `EndCtxGarbage` + +## Improvements + +- Improve the document +- Update the library description sentence in `README.md` and `/src/lib.rs` + +## Internal + +- Update `.gitignore` + # v0.4.0 2023-06-29 ## Changes @@ -12,7 +39,7 @@ This has significantly changed the structure around records. ## Improvements -- Make `ResponseError` a standard error type by [@jlkn]([Title](https://github.com/jlkn)) in [[#2](https://github.com/Rinrin0413/tetr-ch-rs/pull/2)] +- Make `ResponseError` a standard error type by [@jlkn](https://github.com/jlkn) in [[#2](https://github.com/Rinrin0413/tetr-ch-rs/pull/2)] # v0.3.5 2023-05-23 diff --git a/Cargo.toml b/Cargo.toml index 46b12a9..67d1c39 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "tetr_ch" description = "A library for the TETRA CHANNEL API." -version = "0.4.0" +version = "0.5.0" authors = ["Rinrin.rs "] license-file = "LICENSE" repository = "https://github.com/Rinrin0413/tetr-ch-rs.git" @@ -11,6 +11,7 @@ edition = "2021" [dependencies] http = "0.2.8" +serde_json = "1.0.108" [dependencies.reqwest] version = "0.11.11" diff --git a/README.md b/README.md index 6878c06..c80b33c 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ You can get the following from the TETRA CHANNEL API with this library: Also you can search for [TETR.IO](https://tetr.io) accounts by Discord account. -But TETRA CHANNEL API is alpha version. +But TETRA CHANNEL API is in alpha. So this library may not work properly in the future:( **\* This library is NOT official.** diff --git a/examples/ex01_get_user_details/src/main.rs b/examples/ex01_get_user_details/src/main.rs index 685dfc2..7391dff 100644 --- a/examples/ex01_get_user_details/src/main.rs +++ b/examples/ex01_get_user_details/src/main.rs @@ -23,7 +23,7 @@ async fn main() { println!("Rank: {}", usr.data.as_ref().unwrap().user.league.rank.as_str()); println!("Rank icon: {}", usr.data.as_ref().unwrap().user.league.rank.icon_url()); println!("Rank color: {}", usr.data.as_ref().unwrap().user.league.rank.color()); - println!("Reached {:.2}%", usr.rank_progress().unwrap()); + //println!("Reached {:.2}%", usr.rank_progress().unwrap()); The user "RINRIN-RS" may be unranked. println!("№{}", usr.data.as_ref().unwrap().user.league.standing); println!("№{} (local)", usr.data.as_ref().unwrap().user.league.standing_local); println!("Badges count: {}", usr.badges_count()); diff --git a/examples/ex04_get_user_records/src/main.rs b/examples/ex04_get_user_records/src/main.rs index 8257953..922be09 100644 --- a/examples/ex04_get_user_records/src/main.rs +++ b/examples/ex04_get_user_records/src/main.rs @@ -29,6 +29,9 @@ async fn main() { .as_ref() .unwrap() .endcontext + .clone() + .single_play() + .unwrap() .final_time .unwrap() / 1000. @@ -49,6 +52,9 @@ async fn main() { .as_ref() .unwrap() .endcontext + .clone() + .single_play() + .unwrap() .score .unwrap() ); diff --git a/src/client.rs b/src/client.rs index 742be77..185ea36 100644 --- a/src/client.rs +++ b/src/client.rs @@ -444,20 +444,19 @@ impl Client { /// - `stream_type`: /// /// The type of Stream. - /// Currently [`StreamType::FortyLines`], [`StreamType::Blitz`], [`StreamType::Any`], or [`StreamType::League`]. - /// [`StreamType::FortyLines`]: stream::StreamType::FortyLines - /// [`StreamType::Blitz`]: stream::StreamType::Blitz - /// [`StreamType::Any`]: stream::StreamType::Any - /// [`StreamType::League`]: stream::StreamType::League + /// Currently + /// [`StreamType::FortyLines`](stream::StreamType::FortyLines), + /// [`StreamType::Blitz`](stream::StreamType::Blitz), + /// [`StreamType::Any`](stream::StreamType::Any), + /// or [`StreamType::League`](stream::StreamType::League). /// /// - `stream_context`: /// /// The context of the Stream. - /// Currently [`StreamContext::Global`], [`StreamContext::UserBest`], or [`StreamContext::UserRecent`]. - /// - /// [`StreamContext::Global`]: stream::StreamContext::Global - /// [`StreamContext::UserBest`]: stream::StreamContext::UserBest - /// [`StreamContext::UserRecent`]: stream::StreamContext::UserRecent + /// Currently + /// [`StreamContext::Global`](stream::StreamContext::Global), + /// [`StreamContext::UserBest`](stream::StreamContext::UserBest), + /// or [`StreamContext::UserRecent`](stream::StreamContext::UserRecent). /// /// - `stream_identifier` (Optional): /// diff --git a/src/lib.rs b/src/lib.rs index c53a97d..92f0485 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -15,7 +15,7 @@ //! //! Also you can search for [TETR.IO](https://tetr.io) accounts by Discord account. //! -//! But TETRA CHANNEL API is alpha version. +//! But TETRA CHANNEL API is in alpha. //! So this library may not work properly in the future:( //! //! **\* This library is NOT official.** diff --git a/src/model/record.rs b/src/model/record.rs index cf24f7c..07d8d9c 100644 --- a/src/model/record.rs +++ b/src/model/record.rs @@ -145,6 +145,9 @@ pub mod single_play_end_ctx { /// The number of maximum Back To Back chain (zero indexed). #[serde(rename = "topbtb")] pub top_btb: Option, + /// + #[serde(rename = "currentbtbchainpower")] + pub current_btb_chain_power: Option, /// The number of T-Spins. #[serde(rename = "tspins")] pub t_spins: Option, @@ -170,9 +173,7 @@ pub mod single_play_end_ctx { impl SinglePlayEndCtx { //! # Warning //! - //! Calling these methods from a [`Record`] retrieved from other than [`.get_user_records()`] is deprecated. - //! - //! [`.get_user_records()`]: crate::client::Client::get_user_records + //! Calling these methods from a [`Record`] retrieved from other than method [`get_user_records`](crate::client::Client::get_user_records) is deprecated. //! //! These are because the docs for the [TETRA CHANNEL API](https://tetr.io/about/api/) are incomplete, //! so we cannot guarantee which values are passed. @@ -293,6 +294,8 @@ pub mod single_play_end_ctx { pub triples: Option, /// The number of cleared with Quads pub quads: Option, + /// The number of cleared with Pentas + pub pentas: Option, /// The number of cleared with Realt T-Spins #[serde(rename = "realtspins")] pub realt_spins: Option, @@ -317,6 +320,9 @@ pub mod single_play_end_ctx { /// The number of cleared with T-Spin Quads #[serde(rename = "tspinquads")] pub t_spin_quads: Option, + /// The number of cleared with T-Spin Pentas + #[serde(rename = "tspinpentas")] + pub t_spin_pentas: Option, /// The number of cleared with All Clears pub all_clears: Option, } @@ -336,9 +342,16 @@ pub mod single_play_end_ctx { /// The number of garbage received. pub received: Option, /// The number of garbage attacks. - pub attack: Option, + #[serde(rename = "attack")] + pub attacks: Option, /// The number of garbage cleared. pub cleared: Option, + /// The number of garbage attacks. + #[deprecated( + since = "0.5.0", + note = "This field name is not appropriate. This field cannot be used anymore, so use `attacks` instead" + )] + pub attack: Option, } impl AsRef for EndCtxGarbage { diff --git a/src/model/server_stats.rs b/src/model/server_stats.rs index 3d11363..7b5bce4 100644 --- a/src/model/server_stats.rs +++ b/src/model/server_stats.rs @@ -54,9 +54,7 @@ impl ServerStatsResponse { } } - /// Returns the reference to the [`&ServerStats`]. - /// - /// [`&ServerStats`]: crate::model::server_stats::ServerStats + /// Returns the reference to the [`&ServerStats`](crate::model::server_stats::ServerStats). /// /// # Panics /// diff --git a/src/model/user.rs b/src/model/user.rs index 4a54807..230c09b 100644 --- a/src/model/user.rs +++ b/src/model/user.rs @@ -8,7 +8,7 @@ use crate::{ league::LeagueData, record::{single_play_end_ctx::SinglePlayEndCtx, EndContext, Record}, }, - util::{max_f64, to_unix_ts}, + util::{deserialize_from_non_str_to_none, max_f64, to_unix_ts}, }; use serde::Deserialize; use std::fmt::{self, Display, Formatter}; @@ -326,9 +326,7 @@ impl UserResponse { } } - /// Returns the [`&User`]. - /// - /// [`&User`]: crate::model::user::User + /// Returns the [`&User`](crate::model::user::User). /// /// # Panics /// @@ -732,8 +730,15 @@ pub struct Badge { /// The badge's label, shown when hovered. pub label: String, /// The badge's timestamp, if shown. - #[serde(rename = "ts")] + /// + /// Why it uses `deserialize_with` attribute? + /// See [this issue](https://github.com/Rinrin0413/tetr-ch-rs/issues/4). + #[serde(rename = "ts", deserialize_with = "deserialize_from_non_str_to_none")] pub received_at: Option, + /// The badge's group, if specified. + /// + /// ***This property is not said in the [API document](https://tetr.io/about/api).** + pub group: Option, } impl Badge { @@ -1022,9 +1027,7 @@ impl UserRecordsResponse { } } - /// Returns the [`&RecordsData`]. - /// - /// [`&RecordsData`]: crate::model::user::RecordsData + /// Returns the [`&RecordsData`](crate::model::user::RecordsData). /// /// # Panics /// @@ -1472,9 +1475,7 @@ impl FortyLines { self.get_record().recorded_at() } - /// Returns the [`&Record`] for 40 LINES.. - /// - /// [`&Record`]: crate::model::record::Record + /// Returns the [`&Record`](crate::model::record::Record) for 40 LINES. /// /// # Panics /// @@ -1487,9 +1488,9 @@ impl FortyLines { } } - /// Returns the [`&SinglePlayEndCtx`] for 40 LINES. - /// - /// [`&SinglePlayEndCtx`]: crate::model::record::single_play_end_ctx::SinglePlayEndCtx + /// Returns the + /// [`&SinglePlayEndCtx`](crate::model::record::single_play_end_ctx::SinglePlayEndCtx) + /// for 40 LINES. /// /// # Panics /// @@ -1593,9 +1594,7 @@ impl Blitz { self.get_record().recorded_at() } - /// Returns the [`&Record`] for BLITZ. - /// - /// [`&Record`]: crate::model::record::Record + /// Returns the [`&Record`](crate::model::record::Record) for BLITZ. /// /// # Panics /// @@ -1608,9 +1607,9 @@ impl Blitz { } } - /// Returns the [`&SinglePlayEndCtx`] for BLITZ. - /// - /// [`&SinglePlayEndCtx`]: crate::model::record::single_play_end_ctx::SinglePlayEndCtx + /// Returns the + /// [`&SinglePlayEndCtx`](crate::model::record::single_play_end_ctx::SinglePlayEndCtx) + /// for BLITZ. /// /// # Panics /// diff --git a/src/util/mod.rs b/src/util/mod.rs index 270fc50..e77dedd 100644 --- a/src/util/mod.rs +++ b/src/util/mod.rs @@ -1,6 +1,8 @@ //! Utilities for the tetr-ch-rs. use chrono::DateTime; +use serde::Deserialize; +use serde_json::Value; /// Parses a RFC 3339 and ISO 8601 date to UNIX timestamp as `i64`. pub(crate) fn to_unix_ts(ts: &str) -> i64 { @@ -19,6 +21,24 @@ pub(crate) fn max_f64(v1: f64, v2: f64) -> f64 { } } +/// Deserialize from the given value to `Option`. +/// +/// If the given value is string, returns `Some(String)`. +/// Otherwise, returns `None`. +pub(crate) fn deserialize_from_non_str_to_none<'de, D>( + deserializer: D, +) -> Result, D::Error> +where + D: serde::Deserializer<'de>, +{ + let value: Value = Deserialize::deserialize(deserializer)?; + if let Some(received_at) = value.as_str() { + Ok(Some(received_at.to_owned())) + } else { + Ok(None) + } +} + #[cfg(test)] mod tests { use super::*;