use std::ops::{Deref, DerefMut};
use crate::traits::{GetFetchProp, PropFetchable, FetchFrom};
use crate::http::routes::Route;
use crate::util::{fetch_route, a_fetch_route, auto_hashtag};
use serde::{self, Serialize, Deserialize};
use crate::error::Result;
use crate::serde::one_default;
#[cfg(feature = "async")]
use async_trait::async_trait;
use crate::http::Client;
use super::player::Player;
use crate::TimeLike;
#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize, Deserialize)]
pub struct BattleLog {
#[serde(skip)] pub tag: String,
#[serde(default)]
pub items: Vec<Battle>
}
impl Deref for BattleLog {
type Target = Vec<Battle>;
fn deref(&self) -> &Vec<Battle> {
&self.items
}
}
impl DerefMut for BattleLog {
fn deref_mut(&mut self) -> &mut Vec<Battle> {
&mut self.items
}
}
impl GetFetchProp for BattleLog {
type Property = str;
fn get_fetch_prop(&self) -> &str {
&*self.tag
}
fn get_route(tag: &str) -> Route {
Route::PlayerBattlelogs(auto_hashtag(tag))
}
}
#[cfg_attr(feature = "async", async_trait)]
impl FetchFrom<Player> for BattleLog {
fn fetch_from(client: &Client, player: &Player) -> Result<BattleLog> {
BattleLog::fetch(client, &player.tag)
}
#[cfg(feature = "async")]
async fn a_fetch_from(client: &Client, player: &Player) -> Result<BattleLog> {
BattleLog::a_fetch(client, &player.tag).await
}
}
#[cfg_attr(feature = "async", async_trait)]
impl PropFetchable for BattleLog {
type Property = str;
fn fetch(client: &Client, tag: &str) -> Result<BattleLog> {
let route = Self::get_route(tag);
let mut battle_log = fetch_route::<BattleLog>(client, &route)?;
battle_log.tag = tag.to_owned();
Ok(battle_log)
}
#[cfg(feature="async")]
async fn a_fetch(client: &Client, tag: &'async_trait str) -> Result<BattleLog>
where Self: 'async_trait,
Self::Property: 'async_trait,
{
let route = BattleLog::get_route(tag);
let mut battle_log = a_fetch_route::<BattleLog>(client, &route).await?;
battle_log.tag = tag.to_owned();
Ok(battle_log)
}
}
#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Battle {
#[serde(default)]
pub battle_time: TimeLike,
#[serde(default)]
pub event: BattleEvent,
#[serde(default)]
#[serde(rename = "battle")]
pub result: BattleResultInfo,
}
impl Default for Battle {
fn default() -> Battle {
Battle {
battle_time: TimeLike::default(),
event: BattleEvent::default(),
result: BattleResultInfo::default()
}
}
}
#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize, Deserialize)]
pub struct BattleEvent {
#[serde(default)]
pub id: usize,
#[serde(default)]
pub mode: String,
#[serde(default)]
pub map: String,
}
impl BattleEvent {
pub fn new() -> BattleEvent { BattleEvent::default() }
}
impl Default for BattleEvent {
fn default() -> BattleEvent {
BattleEvent {
id: 0,
mode: String::from(""),
map: String::from(""),
}
}
}
#[non_exhaustive]
#[derive(Debug, Clone, Hash, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub enum BattleOutcome {
Victory,
Defeat,
Draw,
}
impl ::std::fmt::Display for BattleOutcome {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
write!(
f,
"{}",
match *self {
BattleOutcome::Victory => "Victory",
BattleOutcome::Defeat => "Defeat",
BattleOutcome::Draw => "Draw",
}
)
}
}
#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct BattleResultInfo {
#[serde(default)]
pub mode: String,
#[serde(default)]
#[serde(rename = "type")]
pub battle_type: Option<String>,
#[serde(default)]
pub duration: usize,
#[serde(default)]
pub trophy_change: isize,
#[serde(default)]
pub rank: Option<u8>,
#[serde(default)]
pub result: Option<BattleOutcome>,
#[serde(default)]
pub star_player: Option<BattlePlayer>,
#[serde(default)]
pub teams: Option<Vec<Vec<BattlePlayer>>>,
#[serde(default)]
pub players: Option<Vec<BattlePlayer>>
}
impl Default for BattleResultInfo {
fn default() -> BattleResultInfo {
BattleResultInfo {
mode: String::from(""),
battle_type: Some(String::from("")),
duration: 0,
trophy_change: 0,
rank: None,
star_player: None,
result: None,
teams: None,
players: None,
}
}
}
#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize, Deserialize)]
pub struct BattlePlayer {
#[serde(default)]
pub tag: String,
#[serde(default)]
pub name: String,
#[serde(default)]
pub brawler: BattleBrawler,
}
impl Default for BattlePlayer {
fn default() -> BattlePlayer {
BattlePlayer {
tag: String::from(""),
name: String::from(""),
brawler: BattleBrawler::default(),
}
}
}
#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize, Deserialize)]
pub struct BattleBrawler {
#[serde(default)]
pub id: usize,
#[serde(default)]
pub name: String,
#[serde(default = "one_default")]
pub power: u8,
#[serde(default)]
pub trophies: usize,
}
impl Default for BattleBrawler {
fn default() -> BattleBrawler {
BattleBrawler {
id: 0,
name: String::from(""),
power: 1,
trophies: 0,
}
}
}
#[cfg(test)]
mod tests {
use serde_json;
use crate::time::TimeLike;
use super::{
BattleLog, BattleBrawler, BattlePlayer, Battle, BattleResultInfo, BattleEvent, BattleOutcome
};
#[test]
fn battlelog_deser() -> Result<(), Box<dyn ::std::error::Error>> {
let battlelog_json_s = r##"{
"items": [
{
"battleTime": "20200131T003432.000Z",
"event": {
"id": 15000163,
"mode": "brawlBall",
"map": "Coarse Course"
},
"battle": {
"mode": "brawlBall",
"type": "ranked",
"result": "victory",
"duration": 96,
"trophyChange": 8,
"starPlayer": {
"tag": "#CCCCCCCC",
"name": "User",
"brawler": {
"id": 16000008,
"name": "NITA",
"power": 10,
"trophies": 500
}
},
"teams": [
[
{
"tag": "#CCCCCCCC",
"name": "User",
"brawler": {
"id": 16000008,
"name": "NITA",
"power": 10,
"trophies": 500
}
},
{
"tag": "#RRRAAALLL",
"name": "Other User",
"brawler": {
"id": 16000001,
"name": "COLT",
"power": 8,
"trophies": 510
}
},
{
"tag": "#GGGGGGGGG",
"name": "Another User",
"brawler": {
"id": 16000018,
"name": "DARRYL",
"power": 10,
"trophies": 520
}
}
],
[
{
"tag": "#777777777",
"name": "User User User",
"brawler": {
"id": 16000032,
"name": "MAX",
"power": 10,
"trophies": 500
}
},
{
"tag": "#SUVSUVSUV",
"name": "User.User?!",
"brawler": {
"id": 16000024,
"name": "ROSA",
"power": 9,
"trophies": 400
}
},
{
"tag": "#QCPJ09J",
"name": "пользователь",
"brawler": {
"id": 16000028,
"name": "SANDY",
"power": 10,
"trophies": 450
}
}
]
]
}
}
]
}"##;
let battle_log = serde_json::from_str::<BattleLog>(battlelog_json_s)?;
assert_eq!(
battle_log,
BattleLog {
items: vec![
Battle {
battle_time: TimeLike(String::from("20200131T003432.000Z")),
event: BattleEvent {
id: 15000163,
mode: String::from("brawlBall"),
map: String::from("Coarse Course")
},
result: BattleResultInfo {
mode: String::from("brawlBall"),
battle_type: Some(String::from("ranked")),
result: Some(BattleOutcome::Victory),
duration: 96,
trophy_change: 8,
star_player: Some(BattlePlayer {
tag: String::from("#CCCCCCCC"),
name: String::from("User"),
brawler: BattleBrawler {
id: 16000008,
name: String::from("NITA"),
power: 10,
trophies: 500
}
}),
teams: Some(vec![
vec![
BattlePlayer {
tag: String::from("#CCCCCCCC"),
name: String::from("User"),
brawler: BattleBrawler {
id: 16000008,
name: String::from("NITA"),
power: 10,
trophies: 500
}
},
BattlePlayer {
tag: String::from("#RRRAAALLL"),
name: String::from("Other User"),
brawler: BattleBrawler {
id: 16000001,
name: String::from("COLT"),
power: 8,
trophies: 510
}
},
BattlePlayer {
tag: String::from("#GGGGGGGGG"),
name: String::from("Another User"),
brawler: BattleBrawler {
id: 16000018,
name: String::from("DARRYL"),
power: 10,
trophies: 520
}
}
],
vec![
BattlePlayer {
tag: String::from("#777777777"),
name: String::from("User User User"),
brawler: BattleBrawler {
id: 16000032,
name: String::from("MAX"),
power: 10,
trophies: 500
}
},
BattlePlayer {
tag: String::from("#SUVSUVSUV"),
name: String::from("User.User?!"),
brawler: BattleBrawler {
id: 16000024,
name: String::from("ROSA"),
power: 9,
trophies: 400
}
},
BattlePlayer {
tag: String::from("#QCPJ09J"),
name: String::from("пользователь"),
brawler: BattleBrawler {
id: 16000028,
name: String::from("SANDY"),
power: 10,
trophies: 450
}
}
]
]), ..BattleResultInfo::default()
}
}
],
tag: String::from(""),
}
);
Ok(())
}
}