rosu_v2/model/
event.rs

1use std::fmt;
2
3use serde::{de, Deserialize};
4use time::OffsetDateTime;
5
6use crate::{Osu, OsuResult};
7
8use super::{
9    beatmap::RankStatus,
10    serde_util,
11    user::{Medal, Username},
12    CacheUserFn, ContainedUsers, GameMode, Grade,
13};
14
15#[derive(Clone, Debug, Deserialize)]
16#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
17pub struct Events {
18    pub events: Vec<Event>,
19    #[serde(rename = "cursor_string", skip_serializing_if = "Option::is_none")]
20    pub cursor: Option<String>,
21    #[serde(skip_serializing_if = "Option::is_none")]
22    pub(crate) sort: Option<EventSort>,
23}
24
25impl Events {
26    /// Returns whether there is a next page of events, retrievable via
27    /// [`get_next`](Events::get_next).
28    #[inline]
29    pub const fn has_more(&self) -> bool {
30        self.cursor.is_some()
31    }
32
33    /// If [`has_more`](Events::has_more) is true, the API can provide the next
34    /// set of events and this method will request them. Otherwise, this method
35    /// returns `None`.
36    pub async fn get_next(&self, osu: &Osu) -> Option<OsuResult<Events>> {
37        let cursor = self.cursor.as_deref()?;
38
39        let fut = osu
40            .events()
41            .cursor(cursor)
42            .sort(self.sort.unwrap_or_default());
43
44        Some(fut.await)
45    }
46}
47
48impl ContainedUsers for Events {
49    fn apply_to_users(&self, f: impl CacheUserFn) {
50        self.events.apply_to_users(f);
51    }
52}
53
54/// The object has different attributes depending on its type.
55#[derive(Clone, Debug, Deserialize)]
56#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
57pub struct Event {
58    #[serde(with = "serde_util::datetime")]
59    pub created_at: OffsetDateTime,
60    #[serde(rename = "id")]
61    pub event_id: u32,
62    #[serde(flatten)]
63    pub event_type: EventType,
64}
65
66impl ContainedUsers for Event {
67    fn apply_to_users(&self, _: impl CacheUserFn) {}
68}
69
70#[derive(Clone, Debug, Deserialize)]
71#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
72pub struct EventBeatmap {
73    pub title: String,
74    pub url: String,
75}
76
77#[derive(Clone, Debug, Deserialize)]
78#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
79pub struct EventBeatmapset {
80    pub title: String,
81    pub url: String,
82}
83
84#[derive(Clone, Debug, Deserialize)]
85#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
86#[serde(rename_all = "camelCase", tag = "type")]
87pub enum EventType {
88    /// When a beatmap has been played for a certain amount of times
89    BeatmapPlaycount { beatmap: EventBeatmap, count: u32 },
90    /// When a beatmapset changes state
91    BeatmapsetApprove {
92        approval: RankStatus,
93        beatmapset: EventBeatmapset,
94        /// Beatmapset owner
95        user: EventUser,
96    },
97    /// When a beatmapset is deleted
98    BeatmapsetDelete { beatmapset: EventBeatmapset },
99    /// When a beatmapset in graveyard is updated
100    BeatmapsetRevive {
101        beatmapset: EventBeatmapset,
102        /// Beatmapset owner
103        user: EventUser,
104    },
105    /// When a beatmapset is updated
106    BeatmapsetUpdate {
107        beatmapset: EventBeatmapset,
108        /// Beatmapset owner
109        user: EventUser,
110    },
111    /// When a new beatmapset is uploaded
112    BeatmapsetUpload {
113        beatmapset: EventBeatmapset,
114        /// Beatmapset owner
115        user: EventUser,
116    },
117    /// When a user obtained a medal
118    #[serde(rename = "achievement")]
119    Medal {
120        #[serde(rename = "achievement")]
121        medal: Medal,
122        user: EventUser,
123    },
124    /// When a user achieves a certain rank on a beatmap
125    Rank {
126        #[serde(rename = "scoreRank")]
127        grade: Grade,
128        rank: u32,
129        mode: GameMode,
130        beatmap: EventBeatmap,
131        user: EventUser,
132    },
133    /// When a user loses first place to another user
134    RankLost {
135        mode: GameMode,
136        beatmap: EventBeatmap,
137        user: EventUser,
138    },
139    /// When a user supports osu! for the second time and onwards
140    #[serde(rename = "userSupportAgain")]
141    SupportAgain { user: EventUser },
142    /// When a user becomes a supporter for the first time
143    #[serde(rename = "userSupportFirst")]
144    SupportFirst { user: EventUser },
145    /// When a user is gifted a supporter tag by another user
146    #[serde(rename = "userSupportGift")]
147    SupportGift {
148        /// Recipient user
149        user: EventUser,
150    },
151    /// When a user changes their username
152    UsernameChange {
153        /// Includes `previous_username`
154        user: EventUser,
155    },
156}
157
158#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
159pub enum EventSort {
160    #[default]
161    IdDescending,
162    IdAscending,
163}
164
165impl EventSort {
166    pub const fn as_str(&self) -> &'static str {
167        match self {
168            Self::IdDescending => "id_desc",
169            Self::IdAscending => "id_asc",
170        }
171    }
172}
173
174impl<'de> de::Deserialize<'de> for EventSort {
175    fn deserialize<D: de::Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
176        struct EventSortVisitor;
177
178        impl de::Visitor<'_> for EventSortVisitor {
179            type Value = EventSort;
180
181            fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
182                f.write_str("id_desc or id_asc")
183            }
184
185            fn visit_str<E: de::Error>(self, v: &str) -> Result<Self::Value, E> {
186                match v {
187                    "id_desc" => Ok(EventSort::IdDescending),
188                    "id_asc" => Ok(EventSort::IdAscending),
189                    _ => Err(de::Error::invalid_value(de::Unexpected::Str(v), &self)),
190                }
191            }
192        }
193
194        d.deserialize_str(EventSortVisitor)
195    }
196}
197
198impl serde::Serialize for EventSort {
199    fn serialize<S: serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
200        s.serialize_str(self.as_str())
201    }
202}
203
204#[derive(Clone, Debug, Deserialize)]
205#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
206pub struct EventUser {
207    pub username: Username,
208    pub url: String,
209    /// Only for `UsernameChange` events
210    #[serde(
211        default,
212        rename = "previousUsername",
213        skip_serializing_if = "Option::is_none"
214    )]
215    pub previous_username: Option<Username>,
216}