Skip to main content

ddapi_rs/scheme/
ddstats.rs

1use crate::scheme::DDSTATS_BASE_URL;
2use crate::util::prelude::{encode, seconds_to_hours, slugify2};
3use chrono::{NaiveDate, NaiveDateTime};
4use serde::{Deserialize, Serialize};
5use std::collections::HashMap;
6
7#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
8pub struct Player {
9    pub points_graph: Vec<PointsGraph>,
10    pub recent_finishes: Vec<RecentFinish>,
11    pub favourite_teammates: Vec<FavouriteTeammate>,
12    pub profile: Profile,
13    pub is_mapper: bool,
14    pub finishes: Vec<Finish>,
15    pub unfinished_maps: Vec<UnfinishedMap>,
16    pub points: Points,
17    #[serde(default)]
18    pub completion_progress: Vec<CompletionProgress>,
19    pub recent_activity: Vec<RecentActivity>,
20    pub recent_player_info: Vec<RecentPlayerInfo>,
21    pub most_played_maps: Vec<MostPlayedMap>,
22    pub most_played_gametypes: Vec<MostPlayed>,
23    pub most_played_categories: Vec<MostPlayed>,
24    pub most_played_locations: Vec<MostPlayed>,
25    pub playtime_per_month: Vec<PlaytimePerMonth>,
26    pub general_activity: Option<GeneralActivity>,
27    pub favourite_rank1s_teammates: Vec<FavouriteRank1sTeammates>,
28    pub all_top_10s: Vec<AllTop10>,
29    pub recent_top_10s: Vec<RecentTop10>,
30}
31
32#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
33pub struct CompletionProgress {
34    pub category: String,
35    pub maps_finished: i64,
36    pub maps_total: i64,
37}
38
39impl Player {
40    #[must_use]
41    pub fn url(&self) -> String {
42        format!(
43            "https://{}/player/{}",
44            DDSTATS_BASE_URL,
45            encode(&self.profile.name)
46        )
47    }
48
49    #[must_use]
50    pub fn url_with_name(player: &str) -> String {
51        format!("https://{}/player/{}", DDSTATS_BASE_URL, encode(player))
52    }
53
54    #[must_use]
55    pub fn api(player: &str) -> String {
56        format!(
57            "https://{}/player/json?player={}",
58            DDSTATS_BASE_URL,
59            encode(player)
60        )
61    }
62}
63
64#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
65pub struct PointsGraph {
66    pub date: NaiveDate,
67    pub points: i64,
68    pub rank_points: i64,
69    pub team_points: i64,
70}
71
72#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
73pub struct RecentFinish {
74    pub map: StatsMap,
75    pub name: String,
76    pub time: f64,
77    pub timestamp: NaiveDateTime,
78    pub server: String,
79    pub cp1: f64,
80    pub cp2: f64,
81    pub cp3: f64,
82    pub cp4: f64,
83    pub cp5: f64,
84    pub cp6: f64,
85    pub cp7: f64,
86    pub cp8: f64,
87    pub cp9: f64,
88    pub cp10: f64,
89    pub cp11: f64,
90    pub cp12: f64,
91    pub cp13: f64,
92    pub cp14: f64,
93    pub cp15: f64,
94    pub cp16: f64,
95    pub cp17: f64,
96    pub cp18: f64,
97    pub cp19: f64,
98    pub cp20: f64,
99    pub cp21: f64,
100    pub cp22: f64,
101    pub cp23: f64,
102    pub cp24: f64,
103    pub cp25: f64,
104    pub team_rank: Option<TeamRankingSMap>,
105    pub rank: Option<RankingSMap>,
106}
107
108#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
109pub struct StatsMap {
110    pub map: String,
111    pub server: String,
112    pub points: i32,
113    pub stars: i32,
114    pub mapper: String,
115    pub timestamp: Option<NaiveDateTime>,
116}
117
118impl StatsMap {
119    #[must_use]
120    pub fn url(&self) -> String {
121        format!(
122            "https://{}/map/{}",
123            DDSTATS_BASE_URL,
124            encode(&slugify2(&self.map))
125        )
126    }
127
128    #[must_use]
129    pub fn url_with_name(map: &str) -> String {
130        format!(
131            "https://{}/map/{}",
132            DDSTATS_BASE_URL,
133            encode(&slugify2(map))
134        )
135    }
136
137    #[must_use]
138    pub fn api() -> String {
139        format!("https://{DDSTATS_BASE_URL}/maps/json")
140    }
141}
142
143#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
144pub struct FavouriteTeammate {
145    pub name: String,
146    pub ranks_together: i64,
147}
148
149#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
150pub struct Profile {
151    pub name: String,
152    pub points: i32,
153    pub clan: Option<String>,
154    pub country: Option<i32>,
155    pub skin_name: Option<String>,
156    pub skin_color_body: Option<i32>,
157    pub skin_color_feet: Option<i32>,
158    pub most_played_location: Option<String>,
159}
160
161impl Profile {
162    #[must_use]
163    pub fn url(&self) -> String {
164        format!("https://{}/player/{}", DDSTATS_BASE_URL, encode(&self.name))
165    }
166
167    #[must_use]
168    pub fn url_with_name(player: &str) -> String {
169        format!("https://{}/player/{}", DDSTATS_BASE_URL, encode(player))
170    }
171
172    #[must_use]
173    pub fn api(player: &str) -> String {
174        format!(
175            "https://{}/profile/json?player={}",
176            DDSTATS_BASE_URL,
177            encode(player)
178        )
179    }
180}
181
182#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
183pub struct Finish {
184    pub map: StatsMap,
185    pub name: String,
186    pub time: f64,
187    pub timestamp: NaiveDateTime,
188    pub server: String,
189    pub rank: i32,
190    pub team_rank: Option<i32>,
191    pub seconds_played: Option<i32>,
192}
193
194#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
195pub struct UnfinishedMap {
196    pub map: StatsMap,
197    pub finishes: Option<i32>,
198    pub finishes_rank: Option<i32>,
199    pub median_time: Option<f64>,
200}
201
202#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
203pub struct Points {
204    pub weekly_points: Option<LeaderboardRank>,
205    pub monthly_points: Option<LeaderboardRank>,
206    pub yearly_points: Option<LeaderboardRank>,
207    pub points: HashMap<Category, Option<LeaderboardRank>>,
208    pub rank_points: HashMap<Category, Option<LeaderboardRank>>,
209    pub team_points: HashMap<Category, Option<LeaderboardRank>>,
210}
211
212#[derive(Default, Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
213pub struct LeaderboardRank {
214    pub points: u64,
215    pub rank: u64,
216}
217
218#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
219pub enum Category {
220    All,
221    Total,
222    Novice,
223    Moderate,
224    Brutal,
225    Insane,
226    Dummy,
227    #[serde(rename = "DDmaX.Easy")]
228    DDmaXEasy,
229    #[serde(rename = "DDmaX.Next")]
230    DDmaXNext,
231    #[serde(rename = "DDmaX.Pro")]
232    DDmaXPro,
233    #[serde(rename = "DDmaX.Nut")]
234    DDmaXNut,
235    Oldschool,
236    Solo,
237    Race,
238    Fun,
239    Event,
240}
241
242#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
243pub struct RecentActivity {
244    pub name: String,
245    pub date: NaiveDate,
246    pub map_name: String,
247    pub map: Option<StatsMap>,
248    pub seconds_played: i64,
249}
250
251#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
252pub struct RecentPlayerInfo {
253    pub name: String,
254    pub clan: String,
255    pub country: i32,
256    pub skin_name: String,
257    pub skin_color_body: Option<i32>,
258    pub skin_color_feet: Option<i32>,
259    pub last_seen: NaiveDate,
260    pub seconds_played: i64,
261}
262
263#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
264pub struct MostPlayedMap {
265    pub map_name: String,
266    pub seconds_played: i64,
267    pub map: Option<StatsMap>,
268}
269
270#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
271pub struct MostPlayedMapsGametype {
272    pub map_name: String,
273    pub seconds_played: i64,
274    pub gametype: String,
275}
276
277#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
278pub struct Teero {
279    pub most_played_maps_gametype: Vec<MostPlayedMapsGametype>,
280}
281
282impl Teero {
283    #[must_use]
284    pub fn api(player: &str) -> String {
285        format!(
286            "https://{}/teero/json?player={}",
287            DDSTATS_BASE_URL,
288            encode(player)
289        )
290    }
291}
292
293#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
294pub struct MostPlayed {
295    pub key: String,
296    pub seconds_played: i64,
297}
298
299#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
300pub struct PlaytimePerMonth {
301    pub year_month: String,
302    pub month: String,
303    pub seconds_played: i64,
304}
305
306#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
307pub struct GeneralActivity {
308    pub total_seconds_played: i64,
309    pub start_of_playtime: NaiveDate,
310    pub average_seconds_played: i64,
311}
312
313impl GeneralActivity {
314    #[must_use]
315    pub fn total_seconds_played_to_hours(&self) -> f64 {
316        // Seconds below 2^53 convert losslessly; playtime is nowhere near
317        // that, so the cast is safe.
318        #[allow(clippy::cast_precision_loss)]
319        let seconds = self.total_seconds_played as f64;
320        seconds_to_hours(seconds)
321    }
322
323    #[must_use]
324    pub fn average_seconds_played_to_hours(&self) -> f64 {
325        #[allow(clippy::cast_precision_loss)]
326        let seconds = self.average_seconds_played as f64;
327        seconds_to_hours(seconds)
328    }
329}
330
331#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
332pub struct FavouriteRank1sTeammates {
333    pub name: String,
334    pub ranks_together: i64,
335}
336
337#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
338pub struct AllTop10 {
339    pub map: StatsMap,
340    pub name: String,
341    pub time: f64,
342    pub rank: i32,
343    pub team_rank: Option<i32>,
344    pub team_time: Option<f64>,
345}
346
347#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
348pub struct RecentTop10 {
349    pub rank_type: String,
350    pub map: String,
351    pub time: f64,
352    pub rank: i32,
353    pub timestamp: NaiveDateTime,
354    pub server: String,
355}
356
357#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
358pub struct InfoSMap {
359    pub map: StatsMap,
360    pub finishes: Option<i32>,
361    pub finishes_rank: Option<i32>,
362    pub median_time: Option<f64>,
363    pub total_playtime: Option<i64>,
364    pub total_playtime_rank: Option<i64>,
365}
366
367#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
368pub struct RankingSMap {
369    pub rank: i32,
370    pub timestamp: Option<NaiveDateTime>,
371    pub name: String,
372    pub time: f64,
373    pub map: String,
374    pub server: String,
375}
376
377#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
378pub struct TeamRankingSMap {
379    pub rank: i32,
380    pub timestamp: Option<NaiveDateTime>,
381    pub id: Vec<u8>,
382    pub players: Vec<String>,
383    pub time: f64,
384    pub map: String,
385    pub server: String,
386}
387
388#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
389pub struct TimeCpsSMap {
390    pub name: String,
391    pub cp1: f64,
392    pub cp2: f64,
393    pub cp3: f64,
394    pub cp4: f64,
395    pub cp5: f64,
396    pub cp6: f64,
397    pub cp7: f64,
398    pub cp8: f64,
399    pub cp9: f64,
400    pub cp10: f64,
401    pub cp11: f64,
402    pub cp12: f64,
403    pub cp13: f64,
404    pub cp14: f64,
405    pub cp15: f64,
406    pub cp16: f64,
407    pub cp17: f64,
408    pub cp18: f64,
409    pub cp19: f64,
410    pub cp20: f64,
411    pub cp21: f64,
412    pub cp22: f64,
413    pub cp23: f64,
414    pub cp24: f64,
415    pub cp25: f64,
416    pub time: f64,
417}
418
419#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
420pub struct PlaytimeSMap {
421    pub rank: i64,
422    pub name: String,
423    pub seconds_played: i64,
424}
425
426#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
427pub struct Map {
428    pub info: InfoSMap,
429    pub rankings: Vec<RankingSMap>,
430    pub team_rankings: Vec<TeamRankingSMap>,
431    pub time_cps: Vec<TimeCpsSMap>,
432    pub playtime: Vec<PlaytimeSMap>,
433}
434
435impl Map {
436    #[must_use]
437    pub fn url(&self) -> String {
438        format!(
439            "https://{}/map/{}",
440            DDSTATS_BASE_URL,
441            encode(&self.info.map.map)
442        )
443    }
444
445    #[must_use]
446    pub fn url_with_name(map: &str) -> String {
447        format!("https://{}/map/{}", DDSTATS_BASE_URL, encode(map))
448    }
449
450    #[must_use]
451    pub fn api(map: &str) -> String {
452        format!("https://{}/map/json?map={}", DDSTATS_BASE_URL, encode(map))
453    }
454}