ugc_scraper_types/
lib.rs

1use std::fmt::Display;
2use std::str::FromStr;
3pub use steamid_ng::SteamID;
4use time::{Date, OffsetDateTime};
5
6#[cfg(feature = "serde")]
7mod serde_date {
8    use serde::de::Error;
9    use serde::ser::Error as _;
10    use serde::{Deserialize, Deserializer, Serialize, Serializer};
11    use time::format_description::FormatItem;
12    use time::macros::format_description;
13    use time::Date;
14
15    const DATE_FORMAT: &[FormatItem<'static>] = format_description!("[year]-[month]-[day]");
16
17    pub fn serialize<S: Serializer>(date: &Date, serializer: S) -> Result<S::Ok, S::Error> {
18        date.format(DATE_FORMAT)
19            .map_err(S::Error::custom)?
20            .serialize(serializer)
21    }
22
23    pub fn deserialize<'de, D: Deserializer<'de>>(deserializer: D) -> Result<Date, D::Error> {
24        let str = <&str>::deserialize(deserializer)?;
25        Date::parse(str, DATE_FORMAT).map_err(D::Error::custom)
26    }
27
28    pub mod opt {
29        use super::DATE_FORMAT;
30        use serde::de::Error;
31        use serde::ser::Error as _;
32        use serde::{Deserialize, Deserializer, Serialize, Serializer};
33        use time::Date;
34
35        pub fn serialize<S: Serializer>(
36            option: &Option<Date>,
37            serializer: S,
38        ) -> Result<S::Ok, S::Error> {
39            option
40                .map(|odt| odt.format(DATE_FORMAT))
41                .transpose()
42                .map_err(S::Error::custom)?
43                .serialize(serializer)
44        }
45
46        pub fn deserialize<'de, D: Deserializer<'de>>(
47            deserializer: D,
48        ) -> Result<Option<Date>, D::Error> {
49            let str = <Option<&str>>::deserialize(deserializer)?;
50            match str {
51                Some(str) => Date::parse(str, DATE_FORMAT)
52                    .map_err(D::Error::custom)
53                    .map(Some),
54                None => Ok(None),
55            }
56        }
57    }
58}
59
60#[derive(Debug, Clone)]
61#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
62pub struct Player {
63    pub name: String,
64    pub avatar: String,
65    #[cfg_attr(feature = "serde", serde(with = "serde_steam_id_as_string"))]
66    pub steam_id: SteamID,
67    pub honors: Vec<Honors>,
68    pub teams: Vec<TeamMemberShip>,
69    pub favorite_classes: Vec<Class>,
70}
71
72#[derive(Debug, Clone)]
73#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
74pub struct Honors {
75    pub format: String,
76    pub season: String,
77    pub team: String,
78}
79
80#[derive(Debug, Clone)]
81#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
82pub struct TeamMemberShip {
83    pub team: TeamRef,
84    pub league: String,
85    #[cfg_attr(feature = "serde", serde(with = "serde_date"))]
86    pub since: Date,
87}
88
89#[derive(Debug, Clone)]
90#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
91pub struct TeamRef {
92    pub name: String,
93    pub id: u32,
94}
95
96#[derive(Debug, Clone)]
97#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
98pub struct MembershipHistory {
99    pub format: String,
100    pub team: TeamRef,
101    pub division: String,
102    #[cfg_attr(feature = "serde", serde(with = "serde_date"))]
103    pub joined: Date,
104    #[cfg_attr(feature = "serde", serde(with = "serde_date::opt"))]
105    pub left: Option<Date>,
106}
107
108#[derive(Debug, Clone)]
109#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
110pub struct Team {
111    pub name: String,
112    pub tag: String,
113    pub image: String,
114    pub format: String,
115    pub timezone: Option<String>,
116    pub steam_group: Option<String>,
117    pub division: String,
118    pub description: String,
119    pub titles: Vec<String>,
120    pub members: Vec<Membership>,
121    pub results: Vec<Record>,
122    pub name_changes: Vec<NameChange>,
123}
124
125#[derive(Debug, Clone)]
126#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
127#[cfg_attr(feature = "serde", serde(rename_all = "lowercase"))]
128pub enum Class {
129    Scout,
130    Soldier,
131    Pyro,
132    Demoman,
133    Heavy,
134    Medic,
135    Sniper,
136    Spy,
137}
138
139impl FromStr for Class {
140    type Err = ();
141
142    fn from_str(s: &str) -> Result<Self, Self::Err> {
143        match s {
144            "scout" => Ok(Class::Scout),
145            "soldier" => Ok(Class::Soldier),
146            "pyro" => Ok(Class::Pyro),
147            "demoman" => Ok(Class::Demoman),
148            "heavy" => Ok(Class::Heavy),
149            "medic" => Ok(Class::Medic),
150            "sniper" => Ok(Class::Sniper),
151            "spy" => Ok(Class::Spy),
152            _ => Err(()),
153        }
154    }
155}
156
157#[derive(Debug, Clone)]
158#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
159pub struct NameChange {
160    pub from_tag: String,
161    pub from: String,
162    pub to_tag: String,
163    pub to: String,
164    #[cfg_attr(feature = "serde", serde(with = "serde_date"))]
165    pub date: Date,
166}
167
168#[derive(Debug, Clone)]
169#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
170pub struct Membership {
171    pub name: String,
172    #[cfg_attr(feature = "serde", serde(with = "serde_steam_id_as_string"))]
173    pub steam_id: SteamID,
174    pub role: String,
175    #[cfg_attr(feature = "serde", serde(with = "time::serde::iso8601"))]
176    pub since: OffsetDateTime,
177}
178
179#[derive(Debug, Clone)]
180#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
181pub struct Record {
182    pub season: u32,
183    pub division: String,
184    pub wins: u8,
185    pub losses: u8,
186}
187
188#[derive(Debug, Clone)]
189#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
190pub struct TeamRosterData {
191    pub steam_group: Option<String>,
192    pub history: Vec<RosterHistory>,
193}
194
195#[derive(Debug, Clone)]
196#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
197pub struct RosterHistory {
198    pub name: String,
199    #[cfg_attr(feature = "serde", serde(with = "serde_steam_id_as_string"))]
200    pub steam_id: SteamID,
201    #[cfg_attr(feature = "serde", serde(with = "serde_date"))]
202    pub joined: Date,
203    #[cfg_attr(feature = "serde", serde(with = "serde_date::opt"))]
204    pub left: Option<Date>,
205}
206
207#[derive(Debug, Clone)]
208#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
209pub struct TeamSeason {
210    pub season: u32,
211    pub matches: Vec<TeamSeasonMatch>,
212}
213
214#[derive(Debug, Clone)]
215#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
216pub struct TeamSeasonMatch {
217    pub division: String,
218    pub week: u8,
219    pub date: String,
220    pub side: String,
221    pub result: MatchResult,
222    pub map: String,
223}
224
225#[derive(Debug, Clone)]
226#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
227pub enum MatchResult {
228    Played {
229        id: u32,
230        opponent: TeamRef,
231        score: u8,
232        score_opponent: u8,
233        match_points: f32,
234        match_points_opponent: f32,
235    },
236    Pending {
237        id: u32,
238        opponent: TeamRef,
239        score: u8,
240        score_opponent: u8,
241    },
242    ByeWeek,
243}
244
245#[derive(Debug, Clone)]
246#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
247pub struct Seasons {
248    pub mode: String,
249    pub seasons: Vec<Season>,
250}
251
252#[derive(Debug, Clone)]
253#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
254pub struct Season {
255    pub id: String,
256    pub name: String,
257}
258
259#[derive(Debug, Clone)]
260#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
261pub struct MatchInfo {
262    pub comment: Option<String>,
263    pub comment_author: Option<String>,
264    pub team_home: TeamRef,
265    pub team_away: TeamRef,
266    pub score_home: u8,
267    pub score_away: u8,
268}
269
270#[derive(Debug, Clone, Copy)]
271pub enum GameMode {
272    Highlander,
273    Sixes,
274    Fours,
275    Ultiduo,
276}
277
278impl FromStr for GameMode {
279    type Err = ();
280
281    fn from_str(s: &str) -> Result<Self, Self::Err> {
282        match s {
283            "9v9" => Ok(GameMode::Highlander),
284            "6v6" => Ok(GameMode::Sixes),
285            "4v4" => Ok(GameMode::Fours),
286            "2v2" => Ok(GameMode::Ultiduo),
287            _ => Err(()),
288        }
289    }
290}
291
292impl GameMode {
293    pub fn letter(&self) -> char {
294        match self {
295            GameMode::Highlander => 'h',
296            GameMode::Sixes => '6',
297            GameMode::Fours => '4',
298            GameMode::Ultiduo => '2',
299        }
300    }
301
302    fn as_str(&self) -> &'static str {
303        match self {
304            GameMode::Highlander => "9v9",
305            GameMode::Sixes => "6v6",
306            GameMode::Fours => "4v4",
307            GameMode::Ultiduo => "2v2",
308        }
309    }
310}
311
312impl Display for GameMode {
313    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
314        f.write_str(self.as_str())
315    }
316}
317
318#[derive(Debug, Clone)]
319#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
320pub struct Transaction {
321    pub name: String,
322    #[cfg_attr(feature = "serde", serde(with = "serde_steam_id_as_string"))]
323    pub steam_id: SteamID,
324    pub action: TransactionAction,
325    pub team: TeamRef,
326}
327
328#[derive(Debug, Clone)]
329#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
330pub enum TransactionAction {
331    Joined,
332    Left,
333}
334
335/// Tried to parse in invalid transaction action
336pub struct MallFormedTransaction {
337    pub text: String,
338}
339
340impl FromStr for TransactionAction {
341    type Err = MallFormedTransaction;
342
343    fn from_str(s: &str) -> Result<Self, Self::Err> {
344        match s {
345            "Joined" => Ok(TransactionAction::Joined),
346            "Left" => Ok(TransactionAction::Left),
347            _ => Err(MallFormedTransaction {
348                text: s.to_string(),
349            }),
350        }
351    }
352}
353
354#[derive(Debug, Clone)]
355#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
356pub struct MapHistory {
357    pub current: CurrentSeasonMapList,
358    pub previous: Vec<PreviousSeasonMapList>,
359}
360
361#[derive(Debug, Clone)]
362#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
363pub struct CurrentSeasonMapList {
364    pub season: u8,
365    pub maps: Vec<CurrentSeasonMap>,
366}
367#[derive(Debug, Clone)]
368#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
369pub struct PreviousSeasonMapList {
370    pub season: u8,
371    pub maps: Vec<PreviousSeasonMap>,
372}
373
374#[derive(Debug, Clone)]
375#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
376pub struct CurrentSeasonMap {
377    pub week: u8,
378    pub map: String,
379    pub date: String,
380    pub na_date: Option<String>,
381}
382
383#[derive(Debug, Clone)]
384#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
385pub struct PreviousSeasonMap {
386    pub week: u8,
387    pub map: String,
388    #[cfg_attr(feature = "serde", serde(with = "serde_date"))]
389    pub date: Date,
390}
391
392#[cfg(feature = "serde")]
393mod serde_steam_id_as_string {
394    use serde::{Deserialize, Deserializer, Serialize, Serializer};
395    use steamid_ng::SteamID;
396
397    pub fn serialize<S: Serializer>(steam_id: &SteamID, serializer: S) -> Result<S::Ok, S::Error> {
398        let id = u64::from(*steam_id);
399        format!("{id}").serialize(serializer)
400    }
401
402    #[allow(dead_code)]
403    pub fn deserialize<'de, D>(deserializer: D) -> Result<SteamID, D::Error>
404    where
405        D: Deserializer<'de>,
406    {
407        let id = u64::deserialize(deserializer)?;
408        Ok(SteamID::from(id))
409    }
410}