1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
use std::collections::{BTreeMap, HashMap};

use chrono::{DateTime, Utc};
use serde::Deserialize;

use torn_api_macros::ApiCategory;

use crate::de_util::{self, null_is_empty_dict};

pub use crate::common::{Attack, AttackFull, LastAction, Status, Territory};

#[derive(Debug, Clone, Copy, ApiCategory)]
#[api(category = "faction")]
#[non_exhaustive]
pub enum FactionSelection {
    #[api(type = "Basic", flatten)]
    Basic,

    #[api(type = "BTreeMap<i32, Attack>", field = "attacks")]
    AttacksFull,

    #[api(type = "BTreeMap<i32, AttackFull>", field = "attacks")]
    Attacks,

    #[api(
        type = "HashMap<String, Territory>",
        field = "territory",
        with = "null_is_empty_dict"
    )]
    Territory,
}

pub type Selection = FactionSelection;

#[derive(Debug, Clone, Deserialize)]
pub struct Member<'a> {
    pub name: &'a str,
    pub level: i16,
    pub days_in_faction: i16,
    pub position: &'a str,
    pub status: Status<'a>,
    pub last_action: LastAction,
}

#[derive(Debug, Clone, Deserialize)]
pub struct FactionTerritoryWar<'a> {
    pub territory_war_id: i32,
    pub territory: &'a str,
    pub assaulting_faction: i32,
    pub defending_faction: i32,
    pub score: i32,
    pub required_score: i32,

    #[serde(with = "chrono::serde::ts_seconds")]
    pub start_time: DateTime<Utc>,

    #[serde(with = "chrono::serde::ts_seconds")]
    pub end_time: DateTime<Utc>,
}

#[derive(Debug, Clone, Deserialize)]
pub struct Basic<'a> {
    #[serde(rename = "ID")]
    pub id: i32,
    pub name: &'a str,
    pub leader: i32,

    pub respect: i32,
    pub age: i16,
    pub capacity: i16,
    pub best_chain: i32,

    #[serde(borrow)]
    pub members: BTreeMap<i32, Member<'a>>,

    #[serde(deserialize_with = "de_util::datetime_map")]
    pub peace: BTreeMap<i32, DateTime<Utc>>,

    #[serde(borrow, deserialize_with = "de_util::empty_dict_is_empty_array")]
    pub territory_wars: Vec<FactionTerritoryWar<'a>>,
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::tests::{async_test, setup, Client, ClientTrait};

    #[async_test]
    async fn faction() {
        let key = setup();

        let response = Client::default()
            .torn_api(key)
            .faction(|b| {
                b.selections(&[Selection::Basic, Selection::Attacks, Selection::Territory])
            })
            .await
            .unwrap();

        response.basic().unwrap();
        response.attacks().unwrap();
        response.attacks_full().unwrap();
        response.territory().unwrap();
    }

    #[async_test]
    async fn faction_public() {
        let key = setup();

        let response = Client::default()
            .torn_api(key)
            .faction(|b| {
                b.id(7049)
                    .selections(&[Selection::Basic, Selection::Territory])
            })
            .await
            .unwrap();

        response.basic().unwrap();
        response.territory().unwrap();
    }

    #[async_test]
    async fn destroyed_faction() {
        let key = setup();

        let response = Client::default()
            .torn_api(key)
            .faction(|b| {
                b.id(8981)
                    .selections(&[Selection::Basic, Selection::Territory])
            })
            .await
            .unwrap();

        response.basic().unwrap();
        response.territory().unwrap();
    }
}