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 #[inline]
29 pub const fn has_more(&self) -> bool {
30 self.cursor.is_some()
31 }
32
33 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#[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 BeatmapPlaycount { beatmap: EventBeatmap, count: u32 },
90 BeatmapsetApprove {
92 approval: RankStatus,
93 beatmapset: EventBeatmapset,
94 user: EventUser,
96 },
97 BeatmapsetDelete { beatmapset: EventBeatmapset },
99 BeatmapsetRevive {
101 beatmapset: EventBeatmapset,
102 user: EventUser,
104 },
105 BeatmapsetUpdate {
107 beatmapset: EventBeatmapset,
108 user: EventUser,
110 },
111 BeatmapsetUpload {
113 beatmapset: EventBeatmapset,
114 user: EventUser,
116 },
117 #[serde(rename = "achievement")]
119 Medal {
120 #[serde(rename = "achievement")]
121 medal: Medal,
122 user: EventUser,
123 },
124 Rank {
126 #[serde(rename = "scoreRank")]
127 grade: Grade,
128 rank: u32,
129 mode: GameMode,
130 beatmap: EventBeatmap,
131 user: EventUser,
132 },
133 RankLost {
135 mode: GameMode,
136 beatmap: EventBeatmap,
137 user: EventUser,
138 },
139 #[serde(rename = "userSupportAgain")]
141 SupportAgain { user: EventUser },
142 #[serde(rename = "userSupportFirst")]
144 SupportFirst { user: EventUser },
145 #[serde(rename = "userSupportGift")]
147 SupportGift {
148 user: EventUser,
150 },
151 UsernameChange {
153 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 #[serde(
211 default,
212 rename = "previousUsername",
213 skip_serializing_if = "Option::is_none"
214 )]
215 pub previous_username: Option<Username>,
216}