1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213
//! A low level api wrapper for Rolimons.com.
//!
//! This crate is a low level wrapper due to the fact that allowed
//! requests to the api are limited. To maintain flexibiliy while also
//! using the api endpoints responsibly, the user is expected to maintain
//! their own caching.
//!
//! All endpoints are accessed from a [`Client`].
//!
//! # API Coverage Checklist
//! - [x] Items API
//! - [`Client::deals_activity`]
//! - [x] Deals API
//! - [`Client::all_item_details`]
//! - [x] Trade Ad API (Partial)
//! - [`Client::create_trade_ad`]
//!
//! # Quick Start
//!
//! This code snippet allows you to get a list of all limited items
//! on Rolimon's, which includes information you would see on an item's page.
//!
//! ```no_run
//! #[tokio::main]
//! async fn main() {
//! let client = roli::ClientBuilder::new().build();
//! let all_item_details = client.all_item_details().await.unwrap();
//! println!("Item Amount: {}", all_item_details.len());
//! }
//! ```
#![warn(missing_docs)]
use serde::{Deserialize, Serialize};
/// A module that contains all the endpoints associated with the deals page.
pub mod deals;
/// A module that contains all the endpoints associated with getting item details.
pub mod items;
/// A module that contains all the endpoints associated with the trade ads page.
pub mod trade_ads;
// Re-export reqwest so people can use the correct version.
pub use reqwest;
const USER_AGENT: &str =
"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:101.0) Gecko/20100101 Firefox/101.0";
/// The universal error used in this crate.
#[derive(thiserror::Error, Debug, Default)]
pub enum RoliError {
/// Used when an endpoint returns `success: false`.
#[error("Request Returned Unsuccessful")]
RequestReturnedUnsuccessful,
/// Used when an endpoint returns status code 429.
#[default]
#[error("Too Many Requests")]
TooManyRequests,
/// Used when an endpoint returns status code 500.
#[error("Internal Server Error")]
InternalServerError,
/// Used when the response from an API endpoint is malformed.
#[error("Malformed Response")]
MalformedResponse,
/// Used when roli_verification contains ASCII characters outside of the range 32-127.
#[error("Roli Verification Contains Invalid Characters")]
RoliVerificationContainsInvalidCharacters,
/// Used when roli_verification is invalid or expired.
#[error("Roli Verification Invalid Or Expired")]
RoliVerificationInvalidOrExpired,
/// Used when roli_verification is not set.
#[error("Roli Verification Not Set")]
RoliVerificationNotSet,
/// Used when a cooldown for something, such as making a trade ad, has not expired.
#[error("Cooldown Not Expired")]
CooldownNotExpired,
/// Used for any status codes that do not fit any enum variants of this error.
/// If you encounter this enum variant, please submit an issue so a variant can be
/// made or the crate can be fixed.
#[error("Unidentified Status Code {0}")]
UnidentifiedStatusCode(u16),
/// Used for any reqwest error that occurs.
#[error("RequestError {0}")]
ReqwestError(reqwest::Error),
}
/// Used for holding either an integer or a string in [`AllItemDetailsResponse`].
/// This is necessary as (for some reason) numbers are represented as strings
/// in the api response.
#[derive(Serialize, Deserialize)]
#[serde(untagged)]
pub(crate) enum Code {
Integer(i64),
String(String),
}
/// Used to interact with the rest of the Rolimons.com api wrapper.
///
/// Contains any necessary authentication and the reqwest client. All
/// [`Client`] methods make exactly one api call.
#[derive(Clone, Debug, Default)]
pub struct Client {
roli_verification: Option<String>,
reqwest_client: reqwest::Client,
}
/// Used to build a [`Client`].
///
/// Creates its own reqwest client if one is not provided to the builder.
#[derive(Clone, Debug, Default)]
pub struct ClientBuilder {
roli_verification: Option<String>,
reqwest_client: Option<reqwest::Client>,
}
impl Code {
/// Returns an i64 inside if the operation was successful, otherwise returns a [`RoliError::MalformedResponse`]
/// (as [`Code`] is only used to parse responses).
fn to_i64(&self) -> Result<i64, RoliError> {
match self {
Self::Integer(x) => Ok(*x),
Self::String(x) => x.parse().map_err(|_| RoliError::MalformedResponse),
}
}
}
impl std::fmt::Display for Code {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Integer(x) => write!(f, "{}", x),
Self::String(x) => write!(f, "{}", x),
}
}
}
impl Client {
/// Constructs a client without providing a roli verification token or custom
/// reqwest client.
///
/// Use [`ClientBuilder`] to add these parameters to a new [`Client`].
pub fn new() -> Self {
Self::default()
}
/// Constructs a new [`Client`] with a roli verification token.
pub fn with_roli_verification(roli_verification: String) -> Self {
Self {
roli_verification: Some(roli_verification),
..Default::default()
}
}
/// Sets the value for the optional `roli_verification` field.
pub fn set_roli_verification(&mut self, roli_verification: String) {
self.roli_verification = Some(roli_verification);
}
/// Returns whether the client has `self.roliverification`
/// set to `Some(_)`. Does not check to see if the token is valid.
pub fn contains_roli_verification(&self) -> bool {
self.roli_verification.is_some()
}
}
impl ClientBuilder {
/// Constructs a new instance of the builder with no values set for its fields.
pub fn new() -> Self {
Self {
roli_verification: None,
reqwest_client: None,
}
}
/// Builds the `Client` struct using the values set in this builder. Uses default values for any unset fields.
pub fn build(self) -> Client {
let reqwest_client = self.reqwest_client.unwrap_or_default();
Client {
roli_verification: self.roli_verification,
reqwest_client,
}
}
/// Sets the value for the optional `roli_verification` field.
///
/// # Examples
///
/// ```
/// # use roli::{ClientBuilder, Client};
/// let builder = ClientBuilder::new();
/// let client = builder.set_roli_verification("apikey".to_string()).build();
/// assert!(client.contains_roli_verification())
/// ```
pub fn set_roli_verification(mut self, roli_verification: String) -> Self {
self.roli_verification = Some(roli_verification);
self
}
/// Sets the value for the optional `reqwest_client` field.
///
/// # Examples
///
/// ```
/// # use roli::{ClientBuilder, Client};
/// let builder = ClientBuilder::new();
/// let reqwest_client = reqwest::Client::new();
/// let client = builder.set_reqwest_client(reqwest_client).build();
/// ```
pub fn set_reqwest_client(mut self, reqwest_client: reqwest::Client) -> Self {
self.reqwest_client = Some(reqwest_client);
self
}
}