Skip to main content

htb_cli/models/
season.rs

1use serde::{Deserialize, Serialize};
2
3use crate::output::Tabular;
4
5#[derive(Debug, Deserialize)]
6pub struct SeasonListResponse {
7    pub data: Vec<Season>,
8}
9
10#[derive(Debug, Deserialize, Serialize)]
11pub struct Season {
12    pub id: u32,
13    pub name: String,
14    #[serde(default)]
15    pub subtitle: Option<String>,
16    #[serde(default)]
17    pub start_date: Option<String>,
18    #[serde(default)]
19    pub end_date: Option<String>,
20    #[serde(default)]
21    pub state: Option<String>,
22    #[serde(default)]
23    pub active: bool,
24    #[serde(default)]
25    pub weeks: Option<u32>,
26    #[serde(default)]
27    pub current_week: Option<u32>,
28    #[serde(default)]
29    pub players: Option<u32>,
30}
31
32#[derive(Debug, Deserialize)]
33pub struct SeasonUserRanksResponse {
34    pub data: Vec<SeasonUserRank>,
35}
36
37#[derive(Debug, Deserialize, Serialize)]
38pub struct SeasonUserRank {
39    #[serde(default)]
40    pub league: Option<String>,
41    #[serde(default)]
42    pub rank: Option<u32>,
43    #[serde(default)]
44    pub total_ranks: Option<u32>,
45    #[serde(default)]
46    pub rank_suffix: Option<String>,
47    #[serde(default)]
48    pub total_season_points: u32,
49    #[serde(default)]
50    pub total_season_bloods: u32,
51    #[serde(default)]
52    pub user_owns: Option<u32>,
53    #[serde(default)]
54    pub root_owns: Option<u32>,
55    #[serde(default)]
56    pub season_id: u32,
57    #[serde(default)]
58    pub season_name: Option<String>,
59}
60
61impl Tabular for Season {
62    fn headers() -> Vec<&'static str> {
63        vec!["ID", "Name", "State", "Weeks", "Players", "Active"]
64    }
65
66    fn row(&self) -> Vec<String> {
67        vec![
68            self.id.to_string(),
69            self.name.clone(),
70            self.state.clone().unwrap_or_default(),
71            self.weeks.map(|w| w.to_string()).unwrap_or_default(),
72            self.players.map(|p| p.to_string()).unwrap_or_default(),
73            if self.active { "✓" } else { "" }.to_string(),
74        ]
75    }
76}
77
78impl Tabular for SeasonUserRank {
79    fn headers() -> Vec<&'static str> {
80        vec![
81            "Season",
82            "League",
83            "Rank",
84            "Points",
85            "User Owns",
86            "Root Owns",
87        ]
88    }
89
90    fn row(&self) -> Vec<String> {
91        vec![
92            self.season_name.clone().unwrap_or_default(),
93            self.league.clone().unwrap_or_else(|| "-".into()),
94            self.rank
95                .map(|r| r.to_string())
96                .unwrap_or_else(|| "-".into()),
97            self.total_season_points.to_string(),
98            self.user_owns
99                .map(|o| o.to_string())
100                .unwrap_or_else(|| "-".into()),
101            self.root_owns
102                .map(|o| o.to_string())
103                .unwrap_or_else(|| "-".into()),
104        ]
105    }
106}
107
108#[cfg(test)]
109mod tests {
110    use super::*;
111
112    #[test]
113    fn deserialize_season_list() {
114        let json = include_str!("../../tests/fixtures/season-list.json");
115        let result: SeasonListResponse = serde_json::from_str(json).unwrap();
116        assert!(!result.data.is_empty());
117        let active = result.data.iter().find(|s| s.active);
118        assert!(active.is_some());
119        assert_eq!(active.unwrap().name, "Season 11");
120    }
121
122    #[test]
123    fn deserialize_season_user_ranks() {
124        let json = include_str!("../../tests/fixtures/season-user-ranks.json");
125        let result: SeasonUserRanksResponse = serde_json::from_str(json).unwrap();
126        assert!(!result.data.is_empty());
127        let first = &result.data[0];
128        assert_eq!(first.league.as_deref(), Some("Silver"));
129        assert_eq!(first.total_season_points, 110);
130    }
131
132    #[test]
133    fn deserialize_unranked_season() {
134        let json = include_str!("../../tests/fixtures/season-user-ranks.json");
135        let result: SeasonUserRanksResponse = serde_json::from_str(json).unwrap();
136        let unranked = result.data.iter().find(|r| r.league.is_none());
137        assert!(unranked.is_some());
138        assert!(unranked.unwrap().rank.is_none());
139    }
140}