Skip to main content

roblox_api/
lib.rs

1pub mod api;
2pub mod challenge;
3pub mod client;
4pub mod ratelimit;
5pub mod validation;
6
7#[macro_use]
8mod endpoint;
9
10use challenge::Challenge;
11use chrono::{Datelike, TimeZone, Utc};
12use serde::{Deserialize, Serialize};
13use strum_macros::{Display, EnumString, FromRepr};
14
15// TODO: using this wrapper as I couldn't figure out how to use chronos datetime alone
16#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
17pub struct DateTime(String);
18impl DateTime {
19    pub fn from_ymd(year: i32, month: u8, day: u8) -> Self {
20        Self(
21            Utc.with_ymd_and_hms(year, month as u32, day as u32, 0, 0, 0)
22                .unwrap()
23                .to_rfc3339(),
24        )
25    }
26
27    pub fn day(&self) -> u8 {
28        chrono::DateTime::parse_from_rfc3339(&self.0).unwrap().day() as u8
29    }
30
31    pub fn month(&self) -> u8 {
32        chrono::DateTime::parse_from_rfc3339(&self.0)
33            .unwrap()
34            .month() as u8
35    }
36
37    pub fn year(&self) -> i32 {
38        chrono::DateTime::parse_from_rfc3339(&self.0)
39            .unwrap()
40            .year()
41    }
42}
43
44#[repr(u8)]
45#[derive(Clone, Copy, Debug, PartialEq, Eq, Display, EnumString, FromRepr)]
46pub enum Gender {
47    None = 1,
48    Male = 2,
49    Female = 3,
50}
51
52#[derive(Debug)]
53pub enum Error {
54    ApiError(ApiError),
55    BadJson,
56    IoError(std::io::Error),
57    ReqwestError(reqwest::Error),
58    #[cfg(feature = "web-socket")]
59    ReqwestWebSocketError(reqwest_websocket::Error),
60}
61
62#[derive(Clone, Debug, PartialEq, Eq, Display)]
63pub enum ApiError {
64    Internal,
65    BadRequest,
66    RequestMissingArgument(String),
67    Ratelimited,
68    Unknown(u16, Option<String>),
69    Unauthorized,
70    InvalidBirthdate,
71    InvalidDisplayName,
72    InvalidGender,
73    InvalidUser,
74    InvalidUserId,
75    PinIsLocked,
76    TokenValidation,
77    CaptchaFailed,
78    ChallengeRequired(Challenge),
79    ChallengeFailed,
80    InvalidChallengeId,
81    InvalidTwoStepVerificationCode,
82    TwoStepVerificationMaintenance,
83    Multiple(Vec<ApiError>),
84    PermissionError,
85    AccontLocked,
86    AccountIssue,
87    InvalidCredentials,
88    UnverifiedCredentials,
89    ExistingLoginSession,
90    DefaultLoginRequired,
91    VNGAppLoginRequired,
92    LuoBuAppLoginRequired,
93    SocialNetworkLoginRequired,
94    InvalidAssetId,
95    InvalidBrowserTrackerId,
96    AlreadyInGroup,
97    AlreadyInGroupRequests,
98    UnsupportedSortOrder,
99    InvalidBadge,
100    ConversationCreationFailed,
101    InvalidConversation,
102    ConversationUserAddFailed,
103    NotEnoughFunds(Currency),
104}
105
106#[repr(u8)]
107#[derive(
108    Copy, Clone, Debug, Deserialize, Serialize, PartialEq, Eq, Display, EnumString, FromRepr,
109)]
110pub enum AssetTypeId {
111    Image = 1,
112    TShirt,
113    Audio,
114    Mesh,
115    Lua,
116    Hat = 8,
117    Place,
118    Model,
119    Shirt,
120    Pants,
121    Decal,
122    Head = 17,
123    Face,
124    Gear,
125    Badge = 21,
126    Animation = 24,
127    Torso = 27,
128    RightArm,
129    LeftArm,
130    LeftLeg,
131    RightLeg,
132    Package,
133    YouTubeVideo,
134    Gamepass,
135    Plugin = 38,
136    MeshPart = 40,
137    HairAccessory,
138    FaceAccessory,
139    NeckAccessory,
140    ShoulderAccessory,
141    FrontAccessory,
142    BackAccessory,
143    WaistAccessory,
144    ClimbAnimation,
145    DeathAnimation,
146    FallAnimation,
147    IdleAnimation,
148    JumpAnimation,
149    RunAnimation,
150    SwimAnimation,
151    WalkAnimation,
152    PoseAnimation,
153    EarAccessory,
154    EyeAccessory,
155    EmoteAnimation = 61,
156    Video,
157    TShirtAccessory = 64,
158    ShirtAccessory,
159    PantsAccessory,
160    JacketAccessory,
161    SweaterAccessory,
162    ShortsAccessory,
163    LeftShoeAccessory,
164    RightShoeAccessory,
165    DressSkirtAccessory,
166    FontFamily,
167    EyebrowAccessory = 76,
168    EyelashAccessory,
169    MoodAnimation,
170    DynamicHead,
171}
172
173#[repr(u8)]
174#[derive(Clone, Debug, Default, Deserialize, Serialize, PartialEq, Eq, Display)]
175pub enum Currency {
176    #[default]
177    Robux = 1,
178    Tickets,
179}
180
181#[derive(Clone, Copy, Debug, Default, Deserialize, Serialize, PartialEq, Eq, Display)]
182pub enum SortOrder {
183    #[default]
184    #[serde(rename = "Asc")]
185    Ascending,
186    #[serde(rename = "Desc")]
187    Descending,
188}
189
190#[derive(Clone, Copy, Debug, PartialEq, Eq)]
191pub struct Paging<'a> {
192    pub cursor: Option<&'a str>,
193    pub limit: Option<u16>,
194    pub order: Option<SortOrder>,
195}
196
197impl From<serde_json::Error> for Error {
198    fn from(_error: serde_json::Error) -> Self {
199        Error::BadJson
200    }
201}
202
203impl From<reqwest::Error> for Error {
204    fn from(error: reqwest::Error) -> Self {
205        Error::ReqwestError(error)
206    }
207}
208
209#[cfg(feature = "web-socket")]
210impl From<reqwest_websocket::Error> for Error {
211    fn from(error: reqwest_websocket::Error) -> Self {
212        Error::ReqwestWebSocketError(error)
213    }
214}
215
216impl std::fmt::Display for DateTime {
217    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
218        write!(f, "{}", self.0)
219    }
220}
221
222impl Default for Paging<'_> {
223    fn default() -> Self {
224        Self {
225            cursor: None,
226            limit: Some(10),
227            order: Some(SortOrder::Ascending),
228        }
229    }
230}
231
232impl<'a> Paging<'a> {
233    pub fn new(
234        cursor: Option<&'a str>,
235        limit: Option<u16>,
236        order: Option<SortOrder>,
237    ) -> Paging<'a> {
238        Self {
239            cursor,
240            limit,
241            order,
242        }
243    }
244}