1macro_rules! into_future {
6 (
8 |$self:ident: $ty:ty| -> $from_bytes:ty { $( $req_body:tt )+ }
9 $( =>
10 |
11 $from_bytes_arg:tt,
12 $post_process_data_arg:tt $(: $post_process_data:ty )?
13 | -> $output:ty { $( $post_process_body:tt )+ }
14 )?
15 ) => {
16 impl crate::future::OsuFutureData for $ty {
17 type FromBytes = $from_bytes;
18 type OsuOutput = into_future!(OUTPUT_TY $from_bytes $( | $output )?);
19 type FromUserData = ();
20 type PostProcessData = into_future!(POST_PROCESS_DATA $( $( $post_process_data )? )?);
21 }
22
23 impl std::future::IntoFuture for $ty {
24 type Output = crate::OsuResult<into_future!(OUTPUT_TY $from_bytes $( | $output )?)>;
25 type IntoFuture = crate::future::OsuFuture<Self>;
26
27 fn into_future($self) -> Self::IntoFuture {
28 let res = { $( $req_body )* };
29 let (req, data) =
30 crate::future::IntoPostProcessData::into_data(res);
31
32 let post_process_fn = into_future!(POST_PROCESS_FN $(
33 |
34 $from_bytes_arg: $from_bytes,
35 $post_process_data_arg $(: $post_process_data )?
36 | { $( $post_process_body )* }
37 )?);
38
39 crate::future::OsuFuture::new(
40 $self.osu,
41 req,
42 data,
43 post_process_fn,
44 )
45 }
46 }
47 };
48
49 (
51 |$self:ident: $ty:ty| -> $from_bytes:ty {
52 $from_user_data:ident {
53 $( $data_field_name:ident: $data_field_ty:ty = $data_field_value:expr, )*
54 }
55 } => |$user_id_arg:tt, $from_user_data_arg:tt| { $( $req_body:tt )* }
56 $(
57 => |$from_bytes_arg:tt, $post_process_data_arg:tt $(: $post_process_data:ty )?|
58 -> $output:ty { $( $post_process_body:tt )* }
59 )?
60 ) => {
61 #[doc(hidden)]
62 pub struct $from_user_data {
63 $( $data_field_name: $data_field_ty, )*
64 }
65
66 impl crate::future::OsuFutureData for $ty {
67 type FromBytes = $from_bytes;
68 type OsuOutput = into_future!(OUTPUT_TY $from_bytes $( | $output )?);
69 type FromUserData = $from_user_data;
70 type PostProcessData = into_future!(POST_PROCESS_DATA $( $( $post_process_data )? )?);
71 }
72
73 impl std::future::IntoFuture for $ty {
74 type Output = crate::OsuResult<into_future!(OUTPUT_TY $from_bytes $( | $output )?)>;
75 type IntoFuture = crate::future::OsuFuture<Self>;
76
77 fn into_future($self) -> Self::IntoFuture {
78 let from_user_data = $from_user_data {
79 $( $data_field_name: $data_field_value, )*
80 };
81
82 let from_user_fn = |$user_id_arg: u32, $from_user_data_arg: $from_user_data| {
83 $( $req_body )*
84 };
85
86 let post_process_fn = into_future!(POST_PROCESS_FN $(
87 |
88 $from_bytes_arg: $from_bytes,
89 $post_process_data_arg $(: $post_process_data )?
90 | { $( $post_process_body )* }
91 )?);
92
93 crate::future::OsuFuture::from_user_id(
94 $self.osu,
95 $self.user_id,
96 from_user_data,
97 from_user_fn,
98 (), post_process_fn,
100 )
101 }
102 }
103 };
104
105 ( OUTPUT_TY $output:ty ) => {
108 $output
109 };
110 ( OUTPUT_TY $from_bytes:ty | $output:ty ) => {
111 $output
112 };
113 ( POST_PROCESS_DATA ) => {
114 ()
115 };
116 ( POST_PROCESS_DATA $data:ty ) => {
117 $data
118 };
119 ( POST_PROCESS_FN ) => {
120 crate::future::noop_post_process
121 };
122 ( POST_PROCESS_FN
123 |$from_bytes_arg:tt: $from_bytes:ty, $data_arg:tt $(: $data:ty )?|
124 { $( $post_process_body:tt )* }
125 ) => {
126 |
127 #[allow(unused_mut)]
128 mut $from_bytes_arg: $from_bytes,
129 $data_arg: into_future!(POST_PROCESS_DATA $( $data )?),
130 | { $( $post_process_body )* }
131 };
132}
133
134use itoa::{Buffer, Integer};
135use serde::Serialize;
136
137use crate::routing::Route;
138
139pub use crate::future::OsuFuture;
140
141pub use self::{
142 beatmap::*, comments::*, event::*, forum::*, matches::*, news::*, ranking::*, replay::*,
143 score::*, seasonal_backgrounds::*, user::*, wiki::*,
144};
145
146mod beatmap;
147mod comments;
148mod event;
149mod forum;
150mod matches;
151mod news;
152mod ranking;
153mod replay;
154mod score;
155mod seasonal_backgrounds;
156mod serialize;
157mod user;
158mod wiki;
159
160#[derive(Copy, Clone)]
161pub(crate) enum Method {
162 Get,
163 Post,
164}
165
166impl Method {
167 pub const fn into_hyper(self) -> hyper::Method {
168 match self {
169 Method::Get => hyper::Method::GET,
170 Method::Post => hyper::Method::POST,
171 }
172 }
173}
174
175pub(crate) struct Request {
176 pub query: Option<String>,
177 pub route: Route,
178 pub body: JsonBody,
179 pub api_version: u32,
180}
181
182impl Request {
183 #[allow(clippy::unreadable_literal)]
184 const API_VERSION: u32 = 20220705;
185
186 const fn new(route: Route) -> Self {
187 Self::with_body(route, JsonBody::new())
188 }
189
190 const fn with_body(route: Route, body: JsonBody) -> Self {
191 Self {
192 query: None,
193 route,
194 body,
195 api_version: Self::API_VERSION,
196 }
197 }
198
199 const fn with_query(route: Route, query: String) -> Self {
200 Self::with_query_and_body(route, query, JsonBody::new())
201 }
202
203 const fn with_query_and_body(route: Route, query: String, body: JsonBody) -> Self {
204 Self {
205 query: Some(query),
206 route,
207 body,
208 api_version: Self::API_VERSION,
209 }
210 }
211
212 const fn api_version(&mut self, api_version: u32) {
213 self.api_version = api_version;
214 }
215}
216
217pub(crate) struct JsonBody {
218 inner: Vec<u8>,
219}
220
221impl JsonBody {
222 pub(crate) const fn new() -> Self {
223 Self { inner: Vec::new() }
224 }
225
226 fn push_prefix(&mut self) {
227 let prefix = if self.inner.is_empty() { b'{' } else { b',' };
228 self.inner.push(prefix);
229 }
230
231 fn push_key(&mut self, key: &[u8]) {
232 self.push_prefix();
233 self.inner.push(b'\"');
234 self.inner.extend_from_slice(key);
235 self.inner.extend_from_slice(b"\":");
236 }
237
238 fn push_value(&mut self, value: &[u8]) {
239 self.inner.push(b'\"');
240 self.inner.extend_from_slice(value);
241 self.inner.push(b'\"');
242 }
243
244 pub(crate) fn push_str(&mut self, key: &str, value: &str) {
245 self.inner.reserve(4 + key.len() + 2 + value.len());
246
247 self.push_key(key.as_bytes());
248 self.push_value(value.as_bytes());
249 }
250
251 pub(crate) fn push_int(&mut self, key: &str, int: impl Integer) {
252 let mut buf = Buffer::new();
253 let int = buf.format(int);
254
255 self.inner.reserve(4 + key.len() + int.len());
256
257 self.push_key(key.as_bytes());
258 self.push_value(int.as_bytes());
259 }
260
261 pub(crate) fn into_bytes(mut self) -> Vec<u8> {
262 if !self.inner.is_empty() {
263 self.inner.push(b'}');
264 }
265
266 self.inner
267 }
268}
269
270struct Query;
271
272impl Query {
273 fn encode<T: Serialize>(query: &T) -> String {
274 serde_urlencoded::to_string(query).expect("serde_urlencoded should not fail")
275 }
276}