diff --git a/CHANGELOG.md b/CHANGELOG.md index 351bae2..4ca3e43 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,32 @@ # blue_archive-rs + +## 0.5.1 - 2024-05-04 + +### Additions ✨ + +### Changes 📝 +- Modified displayed portions of `Student` and other struct fields. +- Changed `Released` struct to allow direct access to `japan`, `global` and `china` fields. +- Changed how `age` is obtained and how it is serialized/deserialized. This will be done as soon as it is read/written without a method call. + - Use `student.age` instead of `student.age()`. + +### Fixes ⚒️ + +## 0.5.0 - 2024-04-01 + +### Additions ✨ + +Added the new `blocking` feature. It is not enabled by default, so you must require it if you wish to use it! + +- This uses reqwest's `blocking` feature to handle all requests in a non-asynchronous way. + +### Changes 📝 + +- Changed how some internal deserialization and hashing works in the crate. + +### Fixes ⚒️ + ## 0.4.0 - 2024-03-23 ### Additions ✨ @@ -31,17 +58,3 @@ ### Fixes ⚒️ - Applied a change to the `Student::position` function, was passing in the `Student::armor_type` for some reason... oops! - -## 0.5.0 - 2024-04-01 - -### Additions ✨ - -Added the new `blocking` feature. It is not enabled by default, so you must require it if you wish to use it! - -- This uses reqwest's `blocking` feature to handle all requests in a non-asynchronous way. - -### Changes 📝 - -- Changed how some internal deserialization and hashing works in the crate. - -### Fixes ⚒️ diff --git a/Cargo.toml b/Cargo.toml index 687f039..7ac6406 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "blue_archive" -version = "0.5.0" +version = "0.5.1" edition = "2021" license-file = "LICENSE" description = "A Blue Archive api wrapper for Rust, based off of SchaleDB's data: https://github.com/lonqie/SchaleDB" diff --git a/examples/fetch_equipment.rs b/examples/fetch_equipment.rs index 89a619c..b51e1f8 100644 --- a/examples/fetch_equipment.rs +++ b/examples/fetch_equipment.rs @@ -2,13 +2,15 @@ use blue_archive::{types::equipment::EquipmentCategory, Language}; #[tokio::main] async fn main() -> anyhow::Result<()> { + println!("{:<60} Category", "Name"); for equipment in blue_archive::fetch_all_equipment(Language::English).await? { - println!("{}", equipment.name) + println!("{:<60} {:?}", equipment.name, equipment.category); } let hats = blue_archive::fetch_equipment_by_category(Language::English, EquipmentCategory::Hat) .await?; + println!("\nHats"); for hat in &hats { println!("[{}] -> {}", hat.id, hat.name) } diff --git a/examples/fetch_student.rs b/examples/fetch_student.rs index ab1bea4..12b28df 100644 --- a/examples/fetch_student.rs +++ b/examples/fetch_student.rs @@ -11,7 +11,8 @@ async fn main() -> anyhow::Result<()> { println!("{header}"); println!("{}", "-".repeat(header.len())); let segments = [ - ("age", format!("{}", hina.age())), + ("id", format!("{}", hina.id)), + ("age", format!("{}", hina.age)), ("birthday", hina.birthday.to_string()), ("school", format!("{}", hina.school())), ("club", format!("{}", hina.club())), @@ -36,7 +37,7 @@ async fn main() -> anyhow::Result<()> { let random_student = (blue_archive::fetch_random_student(Language::English).await?).unwrap(); // I wonder who it will be this time? println!( - "The random student of this second is: {}!", + "The random student of this second is: {}!\n", random_student.full_name_last() ); diff --git a/examples/guessing_game.rs b/examples/guessing_game.rs index 1a7d808..cd0d3c8 100644 --- a/examples/guessing_game.rs +++ b/examples/guessing_game.rs @@ -12,7 +12,7 @@ async fn main() -> anyhow::Result<()> { println!("Guessing Game (it's really bad)"); println!("---------------------------"); - println!("See if you can guess the characters based on certain properties.\n\n"); + println!("See if you can guess the characters based on the remaining ones.\n\n"); let chosen = blue_archive::fetch_random_student(Language::English) .await? diff --git a/examples/student_fetcher.rs b/examples/student_fetcher.rs index 834c99a..1538e66 100644 --- a/examples/student_fetcher.rs +++ b/examples/student_fetcher.rs @@ -36,7 +36,7 @@ async fn main() -> anyhow::Result<()> { - full name (surname): {} "#, aru.id, - aru.age(), + aru.age, aru.club(), aru.full_name_last() ); diff --git a/src/api/mod.rs b/src/api/mod.rs index 2704424..3875d08 100644 --- a/src/api/mod.rs +++ b/src/api/mod.rs @@ -1,6 +1,9 @@ -//! The main module where obtaining the data happens. +//! This is where the main asynchronous (and if elligble, blocking) API implementation is. +//! You are able to obtain data about multiple entities in the game here. + #[cfg(feature = "blocking")] pub mod blocking; + pub mod currency; pub mod enemy; pub mod equipment; @@ -27,9 +30,10 @@ pub(crate) mod internal { /// Contains the endpoints for the data, they mainly just represent the path of what data is obtained. #[derive(Debug, Display)] pub enum Endpoint { + _Localization, + _Voice, _Furniture, _Items, - _Localization, Enemies, Equipment, Currency, diff --git a/src/api/summon.rs b/src/api/summon.rs index 93ed9dc..f35d71d 100644 --- a/src/api/summon.rs +++ b/src/api/summon.rs @@ -2,14 +2,19 @@ use std::borrow::Borrow; -use super::{internal::Endpoint, BlueArchiveError, Client, Language, Result, Summon}; +use super::{ + internal::{fetch_response, Endpoint}, + BlueArchiveError, Client, Language, Result, Summon, +}; /// Fetches all **[`Summons`][`Summon`]** from the data. pub async fn fetch_all_summons( language: impl Borrow, ) -> Result, BlueArchiveError> { - let response = - super::internal::fetch_response(&Endpoint::Summons, language.borrow(), &Client::new()) - .await?; - Ok(response.json::>().await?) + Ok( + fetch_response(&Endpoint::Summons, language.borrow(), &Client::new()) + .await? + .json::>() + .await?, + ) } diff --git a/src/filter/student.rs b/src/filter/student.rs index bf91f2c..c4c869b 100644 --- a/src/filter/student.rs +++ b/src/filter/student.rs @@ -7,7 +7,7 @@ use crate::{ /// Used to filter **[`Students`][`Student`]**. pub trait StudentFilter { - /// Filters a borrowed slice of [`Student`], and returns a **[`Vec`]**. + /// Filters a borrowed slice of [`Student`], and returns a **[`Vec<&Student>`]**. fn filter<'a>(&self, students: &'a [Student]) -> Vec<&'a Student>; } @@ -15,7 +15,7 @@ impl StudentFilter for Age { fn filter<'a>(&self, students: &'a [Student]) -> Vec<&'a Student> { students .iter() - .filter(|student| &student.age() == self) + .filter(|student| &student.age == self) .collect() } } diff --git a/src/types/mod.rs b/src/types/mod.rs index 1073210..f32aa2b 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -17,7 +17,7 @@ pub use summons::Summon; /// **A Blue Archive ID**. /// /// Basically wraps around a [`u32`], and exists for representation of an identifier that can be filtered and have extra functionality. -#[derive(Debug, PartialEq, Eq, Hash, Clone)] +#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord, Hash, Clone)] pub struct ID(u32); impl ID { @@ -29,26 +29,7 @@ impl ID { impl std::fmt::Display for ID { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "ID#:({})", self.0) - } -} - -impl Serialize for ID { - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - serializer.serialize_newtype_struct("ID", &self.0) - } -} - -impl<'de> Deserialize<'de> for ID { - fn deserialize(deserializer: D) -> Result - where - D: serde::Deserializer<'de>, - { - let value: u32 = Deserialize::deserialize(deserializer)?; - Ok(Self(value)) + write!(f, "{}", self.0) } } diff --git a/src/types/students/mod.rs b/src/types/students/mod.rs index 8c00681..4d123f1 100644 --- a/src/types/students/mod.rs +++ b/src/types/students/mod.rs @@ -3,13 +3,16 @@ pub mod student; use std::fmt::Display; +use serde::ser::SerializeStruct; + use serde::{Deserialize, Serialize}; pub use student::Student; /// The age of a **[`Student`]**, which can be **[`None`]** or a **[`u8`]**, depending on if the age can be parsed or not. -#[derive(Debug, PartialEq, Eq)] +#[derive(Debug, PartialEq, Eq, Clone, Copy)] pub struct Age(pub Option); impl Age { + /// Returns the underlying value, though if [`None`], it will return `0`. pub fn as_u8(&self) -> u8 { self.0.unwrap_or(0) } @@ -21,42 +24,92 @@ impl From for Age { } } +impl Serialize for Age { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + match self.0 { + Some(v) => serializer.serialize_some::(&v), + None => serializer.serialize_none(), + } + } +} + +impl<'de> Deserialize<'de> for Age { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + let radix = 10; + let mut num_sequence: Vec = vec![]; + for char in String::deserialize(deserializer)?.chars() { + match char.to_digit(radix.into()) { + Some(digit) => num_sequence.push(digit as u8), + None => break, + } + } + Ok(Age((!num_sequence.is_empty()).then_some( + num_sequence.iter().fold(0, |acc, el| acc * radix + el), + ))) + } +} + impl Display for Age { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self.0 { - Some(age) => { - write!(f, "{}", age) - } + Some(age) => write!(f, "{}", age), None => write!(f, "None"), } } } /// The released status of a **[`Student`]**. -#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)] -pub struct Released(bool, bool, bool); - -impl Released { - pub fn japan(&self) -> bool { - self.0 - } +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Released { + pub japan: bool, + pub global: bool, + pub china: bool, +} - pub fn global(&self) -> bool { - self.1 +impl Serialize for Released { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let mut released = serializer.serialize_struct("IsReleased", 3)?; + released.serialize_field("japan", &self.japan)?; + released.serialize_field("global", &self.global)?; + released.serialize_field("china", &self.china)?; + released.end() } +} - pub fn china(&self) -> bool { - self.2 +impl<'de> Deserialize<'de> for Released { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + let vec = Vec::deserialize(deserializer)?; + Ok(Self { + japan: vec[0], + global: vec[1], + china: vec[2], + }) } } impl Display for Released { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "(JP: {} | Global: {})", self.japan(), self.global()) + write!( + f, + "(JP: {}, GL: {}, CN, {})", + self.japan, self.global, self.china + ) } } -/// The height of a student, represented in a [`metric`](`Height.metric`) or [`imperial`](`Height.imperial`) standard. +/// The height of a student, represented in a [`metric`](`Height::metric`) or [`imperial`](`Height::imperial`) standard. #[derive(Debug, PartialEq, Eq)] pub struct Height { /// The student height in metric standard. @@ -68,7 +121,7 @@ impl Display for Height { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!( f, - "(Metric: {} | Imperial: {:?})", + "(Metric: {}, Imperial: {:?})", self.metric, self.imperial ) } diff --git a/src/types/students/student.rs b/src/types/students/student.rs index 66f4956..1a693e5 100644 --- a/src/types/students/student.rs +++ b/src/types/students/student.rs @@ -27,6 +27,9 @@ pub struct Student { /// The name of the student as presented in the data, and can have an associated tag alongside it. /// An example would be **`Toki (Bunny)`**. pub name: String, + /// The **[`Age`]** of the student. + #[serde(alias = "CharacterAge")] + pub age: Age, /// The first name of the student. e.g., Ichinose. #[serde(alias = "PersonalName")] pub first_name: String, @@ -62,7 +65,6 @@ pub struct Student { collection_texture: Option, family_name_ruby: Option, pub school_year: Option, - character_age: String, /// The birthday of the student represented as (Month, Day). pub birthday: String, #[serde(alias = "CharacterSSRNew")] @@ -146,20 +148,6 @@ impl Student { }) } - /// Gets the **[`Age`]** of the student. - pub fn age(&self) -> Age { - let radix = 10; - let mut num_sequence: Vec = vec![]; - for char in self.character_age.chars() { - match char.to_digit(radix.into()) { - Some(digit) => num_sequence.push(digit as u8), - None => break, - } - } - Age((!num_sequence.is_empty()) - .then_some(num_sequence.iter().fold(0, |acc, el| acc * radix + el))) - } - /// Gets the [`Height`] of the [`Student`]. pub fn height(&self) -> Height { Height { @@ -229,10 +217,10 @@ impl std::fmt::Display for Student { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!( f, - "Student : {} :-: {} | Age:{} | School: {}", - self.full_name_last(), + "(ID: {}, Name: {}, Age: {}, School: {})", self.id, - self.age(), + self.full_name_last(), + self.age, self.school() ) }