1use std::collections::HashMap;
2
3use serde::{Deserialize, Serialize};
4
5#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
7pub struct BotId(pub u64);
8#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
10pub struct UserId(pub u64);
11#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
13pub struct GuildId(pub u64);
14
15#[derive(Clone, Debug, Deserialize, Eq, PartialEq)]
17pub struct User {
18 pub id: UserId,
19 pub username: String,
20 pub discriminator: String,
21 pub avatar: Option<String>,
22}
23
24#[derive(Clone, Debug, Deserialize, Eq, PartialEq)]
26#[serde(rename_all = "camelCase")]
27pub struct DetailedUser {
28 pub id: UserId,
29 pub username: String,
30 pub discriminator: String,
31 pub avatar: Option<String>,
32 #[serde(rename = "defAvatar")]
33 pub default_avatar: String,
34 pub bio: Option<String>,
35 pub banner: Option<String>,
36 pub social: Option<Social>,
37 pub color: Option<String>,
38 pub supporter: bool,
39 pub certified_dev: bool,
40 #[serde(rename = "mod")]
41 pub mod_: bool,
42 pub web_mod: bool,
43 pub admin: bool,
44}
45
46#[derive(Clone, Debug, Deserialize, Eq, PartialEq)]
48pub struct Social {
49 pub github: Option<String>,
50 pub instagram: Option<String>,
51 pub reddit: Option<String>,
52 pub twitter: Option<String>,
53 pub youtube: Option<String>,
54}
55
56#[derive(Clone, Debug, Deserialize, Eq, PartialEq)]
58#[serde(rename_all = "camelCase")]
59pub struct Bot {
60 pub id: BotId,
61 pub username: String,
62 pub discriminator: String,
63 pub avatar: Option<String>,
64 #[serde(rename = "defAvatar")]
65 pub default_avatar: String,
66 pub clientid: String,
67 pub lib: String,
68 pub prefix: String,
69 #[serde(rename = "shortdesc")]
70 pub short_desc: String,
71 #[serde(rename = "longdesc")]
72 pub long_desc: Option<String>,
73 pub tags: Vec<String>,
74 pub website: Option<String>,
75 pub support: Option<String>,
76 pub github: Option<String>,
77 pub owners: Vec<UserId>,
78 pub guilds: Vec<GuildId>,
79 pub invite: Option<String>,
80 pub date: String,
81 pub certified_bot: bool,
82 pub vanity: Option<String>,
83 pub shards: Vec<u64>,
84 pub points: u64,
85 pub monthly_points: u64,
86}
87
88#[derive(Clone, Debug, Deserialize, Eq, PartialEq)]
90pub struct Stats {
91 pub server_count: Option<u64>,
92 pub shards: Vec<u64>,
93 pub shard_count: Option<u64>,
94}
95
96#[derive(Debug, Serialize)]
98#[serde(untagged)]
99pub enum ShardStats {
100 Cumulative {
101 server_count: u64,
102 shard_count: Option<u64>,
103 },
104 Shard {
105 server_count: u64,
106 shard_id: u64,
107 shard_count: u64,
108 },
109 Shards {
110 shards: Vec<u64>,
111 },
112}
113
114pub struct Filter(pub(crate) HashMap<&'static str, String>);
116
117impl Default for Filter {
118 fn default() -> Filter {
119 Filter::new()
120 }
121}
122
123impl Filter {
124 pub fn new() -> Filter {
125 Filter(HashMap::with_capacity(4))
126 }
127
128 pub fn limit(mut self, mut limit: u16) -> Filter {
129 if limit > 500 {
130 limit = 500;
131 }
132 self.0.insert("limit", limit.to_string());
133 self
134 }
135
136 pub fn offset(mut self, offset: u16) -> Filter {
137 self.0.insert("offset", offset.to_string());
138 self
139 }
140
141 pub fn sort<T: AsRef<str>>(mut self, field: T, ascending: bool) -> Filter {
142 let mut buf = String::new();
143 if !ascending {
144 buf.push('-');
145 }
146 buf.push_str(field.as_ref());
147 self.0.insert("sort", buf);
148 self
149 }
150
151 pub fn search<T: ToString>(mut self, search: T) -> Filter {
153 self.0.insert("search", search.to_string());
154 self
155 }
156}
157
158#[derive(Clone, Debug, Deserialize, Eq, PartialEq)]
160pub struct Listing {
161 pub results: Vec<Bot>,
162 pub limit: u64,
163 pub offset: u64,
164 pub count: u64,
165 pub total: u64,
166}
167
168#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
170#[serde(rename_all = "camelCase")]
171pub struct Webhook {
172 pub bot: BotId,
173 pub user: UserId,
174 #[serde(rename = "type")]
175 pub kind: WebhookType,
176 #[serde(default)]
177 pub is_weekend: bool,
178 #[serde(skip_serializing_if = "Option::is_none")]
179 pub query: Option<String>,
180}
181
182#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
184#[serde(rename_all = "lowercase")]
185pub enum WebhookType {
186 Upvote,
187 Test,
188}
189
190impl Webhook {
191 pub fn is_test(&self) -> bool {
192 std::matches!(self.kind, WebhookType::Test)
193 }
194}
195
196impl ::std::ops::Index<usize> for Listing {
197 type Output = Bot;
198
199 fn index(&self, index: usize) -> &Self::Output {
200 &self.results[index]
201 }
202}
203
204impl IntoIterator for Listing {
205 type Item = Bot;
206 type IntoIter = ::std::vec::IntoIter<Bot>;
207
208 fn into_iter(self) -> Self::IntoIter {
209 self.results.into_iter()
210 }
211}
212
213impl<'a> IntoIterator for &'a Listing {
214 type Item = &'a Bot;
215 type IntoIter = ::std::slice::Iter<'a, Bot>;
216
217 fn into_iter(self) -> Self::IntoIter {
218 self.results.iter()
219 }
220}
221
222#[derive(Deserialize)]
223pub(crate) struct UserVoted {
224 pub voted: u64,
225}
226
227#[derive(Deserialize)]
228#[serde(rename = "kebab-case")]
229pub(crate) struct Ratelimit {
230 pub retry_after: u32,
231}
232
233macro_rules! impl_snowflake {
234 ($($type:ty),*) => {
235 $(
236 impl $type {
237 pub fn as_u64(&self) -> u64 {
238 self.0
239 }
240 }
241
242 impl ::std::fmt::Display for $type {
243 fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
244 self.0.fmt(f)
245 }
246 }
247
248 impl From<u64> for $type {
249 fn from(v: u64) -> Self {
250 Self(v)
251 }
252 }
253
254 impl<'de> ::serde::de::Deserialize<'de> for $type {
255 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
256 where
257 D: ::serde::de::Deserializer<'de>,
258 {
259 struct Visitor;
260
261 impl<'de> ::serde::de::Visitor<'de> for Visitor {
262 type Value = $type;
263
264 fn expecting(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
265 f.write_str("identifier")
266 }
267
268 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
269 where
270 E: ::serde::de::Error,
271 {
272 v.parse::<u64>().map(Into::into).map_err(|_| {
273 E::custom(format!("invalid {}: value {}", stringify!(u64), v))
274 })
275 }
276 }
277
278 deserializer.deserialize_str(Visitor)
279 }
280 }
281
282 impl ::serde::ser::Serialize for $type {
283 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
284 where
285 S: ::serde::ser::Serializer,
286 {
287 serializer.collect_str(&self.0)
288 }
289 }
290 )*
291 };
292}
293
294impl_snowflake!(BotId, GuildId, UserId);
295
296#[cfg(test)]
297mod tests {
298 use super::*;
299
300 use serde_test::Token;
301
302 #[test]
303 fn webhook_serde() {
304 let value = Webhook {
305 bot: BotId(1),
306 user: UserId(2),
307 kind: WebhookType::Test,
308 is_weekend: false,
309 query: None,
310 };
311
312 serde_test::assert_tokens(
313 &value,
314 &[
315 Token::Struct {
316 name: "Webhook",
317 len: 4,
318 },
319 Token::Str("bot"),
320 Token::Str("1"),
321 Token::Str("user"),
322 Token::Str("2"),
323 Token::Str("type"),
324 Token::UnitVariant {
325 name: "WebhookType",
326 variant: "test",
327 },
328 Token::Str("isWeekend"),
329 Token::Bool(false),
330 Token::StructEnd,
331 ],
332 );
333 }
334
335 #[test]
336 fn webhook_de_no_weekend() {
337 let value = Webhook {
338 bot: BotId(1),
339 user: UserId(2),
340 kind: WebhookType::Test,
341 is_weekend: false,
342 query: None,
343 };
344
345 serde_test::assert_de_tokens(
346 &value,
347 &[
348 Token::Struct {
349 name: "Webhook",
350 len: 3,
351 },
352 Token::Str("bot"),
353 Token::Str("1"),
354 Token::Str("user"),
355 Token::Str("2"),
356 Token::Str("type"),
357 Token::UnitVariant {
358 name: "WebhookType",
359 variant: "test",
360 },
361 Token::StructEnd,
362 ],
363 );
364 }
365}