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}