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::{EnumIter, IntoEnumIterator};
11
12// TODO: using this wrapper as I couldn't figure out how to use chronos datetime alone
13#[derive(Clone, Debug, Deserialize, 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
41impl std::fmt::Display for DateTime {
42    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
43        write!(f, "{}", self.0)
44    }
45}
46
47#[derive(Debug)]
48pub enum Error {
49    ApiError(ApiError),
50    BadResponse,
51    ReqwestError(reqwest::Error),
52    IoError(std::io::Error),
53}
54
55#[derive(Clone, Debug, PartialEq, Eq)]
56pub enum ApiError {
57    Internal,
58    BadRequest,
59    RequestMissingArgument(String),
60    Ratelimited,
61    Unknown(u16),
62    Unauthorized,
63    InvalidBirthdate,
64    InvalidDisplayName,
65    InvalidGender,
66    UserNotFound,
67    InvalidUserId,
68    PinIsLocked,
69    TokenValidation,
70    CaptchaFailed,
71    ChallengeRequired(Challenge),
72    ChallengeFailed,
73    InvalidChallengeId,
74    InvalidTwoStepVerificationCode,
75    TwoStepVerificationMaintenance,
76    Multiple(Vec<ApiError>),
77    PermissionError,
78    AccontLocked,
79    AccountIssue,
80    InvalidCredentials,
81    UnverifiedCredentials,
82    ExistingLoginSession,
83    DefaultLoginRequired,
84    VNGAppLoginRequired,
85    LuoBuAppLoginRequired,
86    SocialNetworkLoginRequired,
87    InvalidAssetId,
88    InvalidBrowserTrackerId,
89    AlreadyInGroup,
90    AlreadyInGroupRequests,
91    UnsupportedSortOrder,
92}
93
94#[repr(u8)]
95#[derive(Copy, Clone, Debug, Deserialize, Serialize, PartialEq, Eq, EnumIter)]
96pub enum AssetTypeId {
97    Image = 1,
98    TShirt,
99    Audio,
100    Mesh,
101    Lua,
102    Hat = 8,
103    Place,
104    Model,
105    Shirt,
106    Pants,
107    Decal,
108    Head = 17,
109    Face,
110    Gear,
111    Badge = 21,
112    Animation = 24,
113    Torso = 27,
114    RightArm,
115    LeftArm,
116    LeftLeg,
117    RightLeg,
118    Package,
119    YouTubeVideo,
120    Gamepass,
121    Plugin = 38,
122    MeshPart = 40,
123    HairAccessory,
124    FaceAccessory,
125    NeckAccessory,
126    ShoulderAccessory,
127    FrontAccessory,
128    BackAccessory,
129    WaistAccessory,
130    ClimbAnimation,
131    DeathAnimation,
132    FallAnimation,
133    IdleAnimation,
134    JumpAnimation,
135    RunAnimation,
136    SwimAnimation,
137    WalkAnimation,
138    PoseAnimation,
139    EarAccessory,
140    EyeAccessory,
141    EmoteAnimation = 61,
142    Video,
143    TShirtAccessory = 64,
144    ShirtAccessory,
145    PantsAccessory,
146    JacketAccessory,
147    SweaterAccessory,
148    ShortsAccessory,
149    LeftShoeAccessory,
150    RightShoeAccessory,
151    DressSkirtAccessory,
152    FontFamily,
153    EyebrowAccessory = 76,
154    EyelashAccessory,
155    MoodAnimation,
156    DynamicHead,
157}
158
159#[repr(u8)]
160#[derive(Clone, Debug, Default, Serialize, PartialEq, Eq)]
161pub enum Currency {
162    #[default]
163    Robux = 1,
164    Tickets,
165}
166
167#[derive(Clone, Copy, Debug, PartialEq, Eq)]
168pub struct Paging<'a> {
169    pub cursor: Option<&'a str>,
170    pub limit: Option<u16>,
171    pub order: Option<SortOrder>,
172}
173
174#[derive(Clone, Copy, Debug, Default, Serialize, PartialEq, Eq)]
175pub enum SortOrder {
176    #[default]
177    #[serde(rename = "Asc")]
178    Ascending,
179    #[serde(rename = "Desc")]
180    Descending,
181}
182
183impl std::fmt::Display for AssetTypeId {
184    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
185        write!(f, "{:?}", self)
186    }
187}
188
189impl TryFrom<&str> for AssetTypeId {
190    type Error = &'static str;
191
192    fn try_from(value: &str) -> Result<Self, Self::Error> {
193        for kind in AssetTypeId::iter() {
194            if kind.to_string().as_str() == value {
195                return Ok(kind);
196            }
197        }
198
199        Err("Failed to convert string to AssetTypeId")
200    }
201}
202
203impl TryFrom<u8> for AssetTypeId {
204    type Error = &'static str;
205
206    fn try_from(value: u8) -> Result<Self, Self::Error> {
207        for kind in AssetTypeId::iter() {
208            if kind as u8 == value {
209                return Ok(kind);
210            }
211        }
212
213        Err("Failed to convert u8 to AssetTypeId")
214    }
215}
216
217impl std::fmt::Display for SortOrder {
218    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
219        write!(
220            f,
221            "{}",
222            match self {
223                SortOrder::Ascending => "Asc",
224                SortOrder::Descending => "Desc",
225            }
226        )
227    }
228}
229
230impl Default for Paging<'_> {
231    fn default() -> Self {
232        Self {
233            cursor: None,
234            limit: Some(10),
235            order: Some(SortOrder::Ascending),
236        }
237    }
238}
239
240impl<'a> Paging<'a> {
241    pub fn new(
242        cursor: Option<&'a str>,
243        limit: Option<u16>,
244        order: Option<SortOrder>,
245    ) -> Paging<'a> {
246        Self {
247            cursor,
248            limit,
249            order,
250        }
251    }
252}