1use serde::de::DeserializeOwned;
2use serde::{Deserialize, Serialize};
3
4use crate::{DateTime, Error, Paging, client::Client};
5
6pub const URL: &str = "https://badges.roblox.com/v1";
7
8#[derive(Copy, Clone, Debug, PartialEq, Eq)]
9pub enum BadgeSortBy {
10 Rank,
11 DateCreated,
12}
13
14#[derive(Copy, Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
16pub enum BadgeCreatorType {
17 User,
18 Group,
19}
20
21#[derive(Copy, Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
22pub enum BadgeAwarderType {
23 Place,
24}
25
26#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
27#[serde(rename_all = "camelCase")]
28pub struct BadgeStatistics {
29 #[serde(rename = "pastDayAwardedCount")]
30 pub awarded_today: u32,
31 #[serde(rename = "awardedCount")]
32 pub awarded_total: u32,
33 pub win_rate_percentage: f32,
34}
35
36#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
37pub struct BadgeCreator {
38 pub id: u64,
39 pub name: String,
40 #[serde(rename = "type")]
41 pub kind: BadgeCreatorType,
42}
43
44#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
45pub struct BadgeAwarder {
46 pub id: u64,
47 #[serde(rename = "type")]
48 pub kind: BadgeAwarderType,
49}
50
51#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
52#[serde(rename_all = "camelCase")]
53pub struct BadgeUniverse {
54 pub id: u64,
55 pub name: String,
56 pub root_place_id: u64,
57}
58
59#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
60#[serde(rename_all = "camelCase")]
61pub struct Badge {
62 pub id: u64,
63 pub name: String,
64 pub description: String,
65
66 pub display_name: String,
67 pub display_description: String,
68
69 pub enabled: bool,
70
71 pub created: DateTime,
72 pub updated: DateTime,
73
74 pub icon_image_id: u64,
75 pub display_icon_image_id: u64,
76
77 pub statistics: BadgeStatistics,
78
79 pub creator: Option<BadgeCreator>,
80 pub awarder: Option<BadgeAwarder>,
81 #[serde(rename = "awardingUniverse")]
82 pub universe: Option<BadgeUniverse>,
83}
84
85#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
86pub struct BadgesResponse {
87 #[serde(rename = "data")]
88 pub badges: Vec<Badge>,
89 #[serde(rename = "nextPageCursor")]
90 pub next_cursor: Option<String>,
91 #[serde(rename = "previousPageCursor")]
92 pub previous_cursor: Option<String>,
93}
94
95impl std::fmt::Display for BadgeSortBy {
96 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
97 write!(f, "{:?}", self)
98 }
99}
100
101async fn badges_generic<Response: DeserializeOwned>(
102 client: &mut Client,
103 path: &str,
104 sort_by: Option<BadgeSortBy>,
105 paging: Paging<'_>,
106) -> Result<Response, Error> {
107 let limit = paging.limit.unwrap_or(10).to_string();
108 let sort_order = paging.order.unwrap_or_default().to_string();
109 let cursor = match paging.cursor {
110 Some(cursor) => cursor.to_string(),
111 None => String::new(),
112 };
113
114 let sort_by = match sort_by {
115 Some(sort_by) => sort_by.to_string(),
116 None => String::new(),
117 };
118
119 let result = client
120 .requestor
121 .client
122 .get(format!("{URL}/{path}/badges"))
123 .query(&[
124 ("sortBy", sort_by),
125 ("limit", limit),
126 ("sortOrder", sort_order),
127 ("cursor", cursor),
128 ])
129 .headers(client.requestor.default_headers.clone())
130 .send()
131 .await;
132
133 let response = client.validate_response(result).await?;
134 client.requestor.parse_json::<Response>(response).await
135}
136
137pub async fn information(client: &mut Client, id: u64) -> Result<Badge, Error> {
138 let result = client
139 .requestor
140 .client
141 .get(format!("{URL}/badges/{id}"))
142 .headers(client.requestor.default_headers.clone())
143 .send()
144 .await;
145
146 let response = client.validate_response(result).await?;
147 client.requestor.parse_json::<Badge>(response).await
148}
149
150pub async fn universe_badges(
151 client: &mut Client,
152 id: u64,
153 sort_by: Option<BadgeSortBy>,
154 paging: Paging<'_>,
155) -> Result<BadgesResponse, Error> {
156 badges_generic::<BadgesResponse>(client, &format!("universes/{id}"), sort_by, paging).await
157}
158
159pub async fn user_badges(
160 client: &mut Client,
161 id: u64,
162 paging: Paging<'_>,
163) -> Result<BadgesResponse, Error> {
164 badges_generic::<BadgesResponse>(client, &format!("users/{id}"), None, paging).await
165}
166
167pub async fn remove(client: &mut Client, id: u64, user_id: u64) -> Result<(), Error> {
168 let result = client
169 .requestor
170 .client
171 .delete(format!("{URL}/user/{user_id}/badges/{id}"))
172 .headers(client.requestor.default_headers.clone())
173 .send()
174 .await;
175
176 let response = client.validate_response(result).await?;
177 client.requestor.parse_json::<()>(response).await
178}
179
180pub async fn authenticated_remove(client: &mut Client, id: u64) -> Result<(), Error> {
181 let result = client
182 .requestor
183 .client
184 .delete(format!("{URL}/user/badges/{id}"))
185 .headers(client.requestor.default_headers.clone())
186 .send()
187 .await;
188
189 let response = client.validate_response(result).await?;
190 client.requestor.parse_json::<()>(response).await
191}