roblox_api/api/badges/
v1.rs

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// TODO: use CreatorType instead
15#[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}