#[cfg(feature = "async")]
use async_trait::async_trait;
use serde::{self, Serialize, Deserialize};
use crate::traits::{PropFetchable, FetchFrom, GetFetchProp};
use crate::error::Result;
#[cfg(feature = "async")]
use crate::util::a_fetch_route;
#[cfg(feature = "players")]
use super::players::PlayerClub;
use crate::http::Client;
use crate::serde::{
serialize_smt_pointer, deserialize_number_from_string, deserialize_default_smt_pointer,
oxffffff_default,
};
use crate::http::routes::Route;
use crate::util::{auto_hashtag, fetch_route};
use std::fmt::{Display, Formatter};
use crate::model::rankings::ClubRanking;
use std::cmp::Ordering;
pub use members::ClubMembers;
#[non_exhaustive]
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub enum ClubType {
Open,
InviteOnly,
Closed,
}
impl Default for ClubType {
fn default() -> ClubType { ClubType::Open }
}
impl Display for ClubType {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
write!(
f, "{}",
match self {
ClubType::Open => "Open",
ClubType::InviteOnly => "InviteOnly",
ClubType::Closed => "Closed",
}
)
}
}
#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Club {
#[serde(default)]
pub tag: String,
#[serde(default)]
pub name: String,
#[serde(default)]
pub description: Option<String>,
#[serde(default)]
pub trophies: usize,
#[serde(default)]
pub required_trophies: usize,
#[serde(default)]
#[serde(serialize_with="serialize_smt_pointer")]
#[serde(deserialize_with="deserialize_default_smt_pointer")]
pub members: ClubMembers,
#[serde(rename = "type")]
#[serde(default)]
pub club_type: ClubType
}
impl Default for Club {
fn default() -> Club {
Club {
tag: String::from(""),
name: String::from(""),
description: None,
trophies: 0,
required_trophies: 0,
members: ClubMembers::default(),
club_type: ClubType::Open,
}
}
}
impl GetFetchProp for Club {
type Property = str;
fn get_fetch_prop(&self) -> &str { &self.tag }
fn get_route(tag: &str) -> Route { Route::Club(auto_hashtag(tag)) }
}
#[cfg_attr(feature = "async", async_trait)]
impl PropFetchable for Club {
type Property = str;
fn fetch(client: &Client, tag: &str) -> Result<Club> {
let route = Club::get_route(tag);
let mut club = fetch_route::<Club>(client, &route)?;
club.members.tag = club.tag.clone();
Ok(club)
}
#[cfg(feature="async")]
async fn a_fetch(client: &Client, tag: &'async_trait str) -> Result<Club>
where Self: 'async_trait,
Self::Property: 'async_trait,
{
let route = Club::get_route(tag);
let mut club = a_fetch_route::<Club>(client, &route).await?;
club.members.tag = club.tag.clone();
Ok(club)
}
}
#[cfg_attr(feature = "async", async_trait)]
#[cfg(feature = "players")]
impl FetchFrom<PlayerClub> for Club {
fn fetch_from(client: &Client, p_club: &PlayerClub) -> Result<Club> {
Club::fetch(client, &p_club.tag)
}
#[cfg(feature = "async")]
async fn a_fetch_from(client: &Client, p_club: &PlayerClub) -> Result<Club> {
Club::a_fetch(client, &p_club.tag).await
}
}
#[cfg_attr(feature = "async", async_trait)]
#[cfg(feature = "rankings")]
impl FetchFrom<ClubRanking> for Club {
fn fetch_from(client: &Client, c_ranking: &ClubRanking) -> Result<Club> {
Club::fetch(client, &c_ranking.tag)
}
#[cfg(feature = "async")]
async fn a_fetch_from(client: &Client, c_ranking: &ClubRanking) -> Result<Club> {
Club::a_fetch(client, &c_ranking.tag).await
}
}
#[non_exhaustive]
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub enum ClubMemberRole {
Member = 0,
Senior = 1,
VicePresident = 2,
President = 3,
}
impl Display for ClubMemberRole {
fn fmt(&self, f: &mut Formatter<'_>) -> ::std::fmt::Result {
write!(
f, "{}",
match *self {
ClubMemberRole::Member => "Member",
ClubMemberRole::Senior => "Senior",
ClubMemberRole::VicePresident => "VicePresident",
ClubMemberRole::President => "President",
}
)
}
}
impl PartialOrd for ClubMemberRole {
fn partial_cmp(&self, other: &ClubMemberRole) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for ClubMemberRole {
fn cmp(&self, other: &ClubMemberRole) -> Ordering {
(*self as u8).cmp(&(*other as u8))
}
}
impl Default for ClubMemberRole {
fn default() -> ClubMemberRole { ClubMemberRole::Member }
}
#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ClubMember {
#[serde(default)]
pub tag: String,
#[serde(default)]
pub name: String,
#[serde(default)]
pub trophies: usize,
#[serde(default)]
pub role: ClubMemberRole,
#[serde(default = "oxffffff_default")]
#[serde(deserialize_with = "deserialize_number_from_string")] pub name_color: u64
}
impl PartialOrd for ClubMember {
fn partial_cmp(&self, other: &ClubMember) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for ClubMember {
fn cmp(&self, other: &ClubMember) -> Ordering {
self.role.cmp(&other.role)
}
}
impl Default for ClubMember {
fn default() -> ClubMember {
ClubMember {
tag: String::from(""),
name: String::from(""),
trophies: 0,
role: ClubMemberRole::default(),
name_color: 0xff_ff_ff
}
}
}
pub mod members {
use super::*;
use std::ops::{Deref, DerefMut};
#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize, Deserialize)]
pub struct ClubMembers {
#[serde(skip)] pub tag: String,
#[serde(default)]
pub items: Vec<ClubMember>
}
impl Deref for ClubMembers {
type Target = Vec<ClubMember>;
fn deref(&self) -> &Vec<ClubMember> {
&self.items
}
}
impl DerefMut for ClubMembers {
fn deref_mut(&mut self) -> &mut Vec<ClubMember> {
&mut self.items
}
}
impl GetFetchProp for ClubMembers {
type Property = str;
fn get_fetch_prop(&self) -> &str {
&*self.tag
}
fn get_route(tag: &str) -> Route {
Route::ClubMembers(auto_hashtag(tag))
}
}
impl From<Club> for ClubMembers {
fn from(club: Club) -> ClubMembers {
club.members
}
}
impl From<&Club> for ClubMembers {
fn from(club: &Club) -> ClubMembers {
club.members.to_owned()
}
}
impl<'a> From<&'a Club> for &'a ClubMembers {
fn from(club: &'a Club) -> Self {
(&club.members) as &'a ClubMembers
}
}
#[cfg_attr(feature = "async", async_trait)]
impl PropFetchable for ClubMembers {
type Property = str;
fn fetch(client: &Client, tag: &str) -> Result<ClubMembers> {
let route = Self::get_route(tag);
let mut members = fetch_route::<ClubMembers>(client, &route)?;
members.tag = tag.to_owned();
Ok(members)
}
#[cfg(feature="async")]
async fn a_fetch(client: &Client, tag: &'async_trait str) -> Result<ClubMembers>
where Self: 'async_trait,
Self::Property: 'async_trait,
{
let route = ClubMembers::get_route(tag);
let mut members = a_fetch_route::<ClubMembers>(client, &route).await?;
members.tag = tag.to_owned();
Ok(members)
}
}
impl Default for ClubMembers {
fn default() -> ClubMembers {
ClubMembers { tag: String::from(""), items: vec![] }
}
}
}
#[cfg(test)]
mod tests {
use std::result::Result as StdResult;
use super::*;
use crate::error::Error as BrawlError;
use serde_json;
#[test]
fn club_deser() -> StdResult<(), Box<dyn ::std::error::Error>> {
let club_json_s = r##"{
"tag": "#GGGGGGG",
"name": "Club",
"description": "Brawl Stars club",
"type": "open",
"requiredTrophies": 1000,
"trophies": 60000,
"members": [
{
"tag": "#PPP200JJJ",
"name": "Member #1",
"nameColor": "0xffff8afb",
"role": "vicePresident",
"trophies": 500
},
{
"tag": "#CCCCCCCCCC",
"name": "Member #2",
"nameColor": "0xff1ba5f5",
"role": "president",
"trophies": 200
},
{
"tag": "#VVVVVVVVV",
"name": "Member #3",
"nameColor": "0xffffff",
"role": "member",
"trophies": 8500
},
{
"tag": "#9999999999",
"name": "Member #4",
"nameColor": "0xff4ddba2",
"role": "member",
"trophies": 20000
},
{
"tag": "#UUUUUU888",
"name": "Member #5",
"nameColor": "0xff1ba5f5",
"role": "senior",
"trophies": 4500
},
{
"tag": "#JJJJJJJJJ",
"name": "Member ██▬█",
"nameColor": "0xff1ba5f5",
"role": "member",
"trophies": 26300
}
]
}"##;
let club: Club = serde_json::from_str::<Club>(club_json_s)
.map_err(BrawlError::Json)?;
assert_eq!(
club,
Club {
tag: String::from("#GGGGGGG"),
name: String::from("Club"),
description: Some(String::from("Brawl Stars club")),
club_type: ClubType::Open,
required_trophies: 1000,
trophies: 60000,
members: ClubMembers {
items: vec![
ClubMember {
tag: String::from("#PPP200JJJ"),
name: String::from("Member #1"),
name_color: 0xffff8afb,
role: ClubMemberRole::VicePresident,
trophies: 500
},
ClubMember {
tag: String::from("#CCCCCCCCCC"),
name: String::from("Member #2"),
name_color: 0xff1ba5f5,
role: ClubMemberRole::President,
trophies: 200
},
ClubMember {
tag: String::from("#VVVVVVVVV"),
name: String::from("Member #3"),
name_color: 0xffffff,
role: ClubMemberRole::Member,
trophies: 8500
},
ClubMember {
tag: String::from("#9999999999"),
name: String::from("Member #4"),
name_color: 0xff4ddba2,
role: ClubMemberRole::Member,
trophies: 20000
},
ClubMember {
tag: String::from("#UUUUUU888"),
name: String::from("Member #5"),
name_color: 0xff1ba5f5,
role: ClubMemberRole::Senior,
trophies: 4500
},
ClubMember {
tag: String::from("#JJJJJJJJJ"),
name: String::from("Member ██▬█"),
name_color: 0xff1ba5f5,
role: ClubMemberRole::Member,
trophies: 26300
}
],
..ClubMembers::default()
}
}
);
Ok(())
}
#[test]
fn club_members_deser() -> StdResult<(), Box<dyn ::std::error::Error>> {
let cm_json_s = r##"{
"items": [
{
"tag": "#PPP200JJJ",
"name": "Member #1",
"nameColor": "0xffff8afb",
"role": "vicePresident",
"trophies": 500
},
{
"tag": "#CCCCCCCCCC",
"name": "Member #2",
"nameColor": "0xff1ba5f5",
"role": "president",
"trophies": 200
},
{
"tag": "#VVVVVVVVV",
"name": "Member #3",
"nameColor": "0xffffff",
"role": "member",
"trophies": 8500
},
{
"tag": "#9999999999",
"name": "Member #4",
"nameColor": "0xff4ddba2",
"role": "member",
"trophies": 20000
},
{
"tag": "#UUUUUU888",
"name": "Member #5",
"nameColor": "0xff1ba5f5",
"role": "senior",
"trophies": 4500
},
{
"tag": "#JJJJJJJJJ",
"name": "Member ██▬█",
"nameColor": "0xff1ba5f5",
"role": "member",
"trophies": 26300
}
],
"paging": {
"cursors": {}
}
}"##;
let club_members: ClubMembers = serde_json::from_str::<ClubMembers>(cm_json_s)
.map_err(BrawlError::Json)?;
assert_eq!(
club_members,
ClubMembers {
items: vec![
ClubMember {
tag: String::from("#PPP200JJJ"),
name: String::from("Member #1"),
name_color: 0xffff8afb,
role: ClubMemberRole::VicePresident,
trophies: 500
},
ClubMember {
tag: String::from("#CCCCCCCCCC"),
name: String::from("Member #2"),
name_color: 0xff1ba5f5,
role: ClubMemberRole::President,
trophies: 200
},
ClubMember {
tag: String::from("#VVVVVVVVV"),
name: String::from("Member #3"),
name_color: 0xffffff,
role: ClubMemberRole::Member,
trophies: 8500
},
ClubMember {
tag: String::from("#9999999999"),
name: String::from("Member #4"),
name_color: 0xff4ddba2,
role: ClubMemberRole::Member,
trophies: 20000
},
ClubMember {
tag: String::from("#UUUUUU888"),
name: String::from("Member #5"),
name_color: 0xff1ba5f5,
role: ClubMemberRole::Senior,
trophies: 4500
},
ClubMember {
tag: String::from("#JJJJJJJJJ"),
name: String::from("Member ██▬█"),
name_color: 0xff1ba5f5,
role: ClubMemberRole::Member,
trophies: 26300
}
],
..ClubMembers::default()
}
);
Ok(())
}
}