roblox_api/
lib.rs

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