1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
use time::OffsetDateTime;
use super::{Pending, UserIdentification};
use crate::{
model::{Beatmap, GameMode, GameMods},
routing::Route,
Osu,
};
/// Retrieve a [`Beatmap`].
pub struct GetBeatmap<'a> {
fut: Option<Pending<'a>>,
osu: Option<&'a Osu>,
creator: Option<UserIdentification>,
hash: Option<String>,
limit: Option<u32>,
map_id: Option<u32>,
mapset_id: Option<u32>,
mode: Option<GameMode>,
mods: Option<GameMods>,
since: Option<OffsetDateTime>,
with_converted: Option<bool>,
}
/// Retrieve [`Beatmap`]s
pub struct GetBeatmaps<'a> {
fut: Option<Pending<'a>>,
osu: Option<&'a Osu>,
creator: Option<UserIdentification>,
hash: Option<String>,
limit: Option<u32>,
map_id: Option<u32>,
mapset_id: Option<u32>,
mode: Option<GameMode>,
mods: Option<GameMods>,
since: Option<OffsetDateTime>,
with_converted: Option<bool>,
}
macro_rules! impl_beatmap {
($name: ident, $default_limit: expr) => {
impl<'a> $name<'a> {
pub(crate) fn new(osu: &'a Osu) -> Self {
Self {
osu: Some(osu),
fut: None,
creator: None,
hash: None,
limit: $default_limit,
map_id: None,
mapset_id: None,
mode: None,
mods: None,
since: None,
with_converted: None,
}
}
/// Optional, specify the creator of the mapset either by id (`u32`) or name (`String`/`&str`).
#[inline]
pub fn creator(mut self, creator: impl Into<UserIdentification>) -> Self {
self.creator.replace(creator.into());
self
}
/// Optional, the beatmap hash e.g. from a replay file.
#[inline]
pub fn hash(mut self, hash: impl Into<String>) -> Self {
self.hash.replace(hash.into());
self
}
/// Optional, amount of results.
/// Default and maximum are 500.
#[inline]
pub fn limit(mut self, limit: u32) -> Self {
self.limit.replace(limit.max(0).min(500));
self
}
/// Optional, specify a beatmap_id
#[inline]
pub fn map_id(mut self, map_id: u32) -> Self {
self.map_id.replace(map_id);
self
}
/// Optional, specify a beatmapset_id
#[inline]
pub fn mapset_id(mut self, mapset_id: u32) -> Self {
self.mapset_id.replace(mapset_id);
self
}
/// Optional, defaults to `GameMode::Osu`
#[inline]
pub fn mode(mut self, mode: GameMode) -> Self {
self.mode.replace(mode);
self
}
/// Optional, mods that applies to the beatmap requested.
/// Multiple mods is supported, but it should not contain any non-difficulty-increasing mods.
#[inline]
pub fn mods(mut self, mods: GameMods) -> Self {
self.mods.replace(mods);
self
}
/// Optional, only ranked/loved beatmaps approved since this date.
#[inline]
pub fn since(mut self, since: OffsetDateTime) -> Self {
self.since.replace(since);
self
}
/// Optional, specify whether converted beatmaps are included.
/// Only has an effect if mode is chosen and not `GameMode::Osu`.
/// Converted maps show their converted difficulty rating.
/// Defaults to 0.
#[inline]
pub fn with_converted(mut self, with_converted: bool) -> Self {
self.with_converted.replace(with_converted);
self
}
fn start(&mut self) {
let route = Route::GetBeatmaps {
creator: self.creator.take(),
hash: self.hash.take().map(String::into_boxed_str),
limit: self.limit.take(),
map_id: self.map_id.take(),
mapset_id: self.mapset_id.take(),
mode: self.mode.take(),
mods: self.mods.take(),
since: self.since.take(),
with_converted: self.with_converted.take(),
};
#[cfg(feature = "metrics")]
self.osu.unwrap().0.metrics.beatmaps.inc();
self.fut
.replace(Box::pin(self.osu.unwrap().request_bytes(route)));
}
}
};
}
impl_beatmap!(GetBeatmaps, None);
poll_vec_req!(GetBeatmaps<'_>, Beatmap);
impl_beatmap!(GetBeatmap, Some(1));
poll_req!(GetBeatmap<'_>, Beatmap);