lead_oxide/
errors.rs

1//! Represents all the errors epressed by the API and library.
2//!
3//! These are divided into an `APIError` which represents all errors returned by pubproxy.com and
4//! `ParamError` which expresses any parameters that were invalid and can't be caught at compile time.
5
6use std::fmt;
7
8use crate::{constants, types::NaiveResponse};
9
10use thiserror::Error;
11
12/// Represents an error with a parameter type.
13///
14/// Currently the only types that can error are [`LastChecked`][crate::types::LastChecked] and
15/// [`TimeToConnect`][crate::types::TimeToConnect] since they are both bounded values which will
16/// error if the provided value is out of bounds.
17#[derive(Error, Debug, PartialEq)]
18pub enum ParamError<T: PartialEq + fmt::Debug> {
19    #[error("'{value:?}' is outside bounds: {bounds:?}")]
20    OutOfBounds { bounds: (T, T), value: T },
21}
22
23impl<T: PartialEq + fmt::Debug> ParamError<T> {
24    pub fn out_of_bounds(value: T, bounds: (T, T)) -> Self {
25        Self::OutOfBounds { value, bounds }
26    }
27}
28
29/// Represents all possible errors returned by the API.
30///
31/// Some variants should be entirely prevented by this library like `Client`, while others are
32/// expected from heavy use like `RateLimit` or from being too strict on parameters like `NoProxy`.
33#[derive(Error, Debug)]
34pub enum ApiError {
35    #[error("Client Error ({status}): {text}\n This should be prevented, please raise an issue")]
36    Client { status: u16, text: String },
37
38    #[error("Internal Server Error ({status}): {text}")]
39    Server { status: u16, text: String },
40
41    #[error("Invalid API key, make sure your key is valid")]
42    ApiKey,
43
44    // TODO: mention fetchers from multiple sessions
45    #[error(
46        "You have exceeded the rate limit. This could be due to multiple programs using the API. \
47 If this is not the case then sorry but the API hates you, consider raising an issue."
48    )]
49    RateLimit,
50
51    #[error("You have exhausted the daily limit of proxies.")]
52    DailyLimit,
53
54    #[error("No matching proxies, consider broadening the parameters used")]
55    NoProxy,
56
57    #[error("The API returned an unexpected message. Consider raising an issue with the library")]
58    Unknown,
59}
60
61impl From<NaiveResponse> for ApiError {
62    fn from(naive_resp: NaiveResponse) -> Self {
63        let NaiveResponse { status, text } = naive_resp;
64
65        // Some known errors get returned with varied `status` codes so match on response text first
66        // then add context to unknown status codes
67        match Self::from(text.clone()) {
68            Self::Unknown => {
69                if (400..500).contains(&status) {
70                    Self::Client { status, text }
71                } else if (500..600).contains(&status) {
72                    Self::Server { status, text }
73                } else {
74                    unreachable!(
75                        "Tried creating ApiError from valid response ({}). Please raise an issue \
76                         at {}.",
77                        status,
78                        constants::REPO_URI
79                    );
80                }
81            }
82            err => err,
83        }
84    }
85}
86
87const INVALID_API_KEY: &str =
88    "Invalid API. Get your API to make unlimited requests at http://pubproxy.com/#premium";
89const RATE_LIMIT: &str = "We have to temporarily stop you. You're requesting proxies a little too \
90                          fast (2+ requests per second). Get your API to remove this limit at
91                          http://pubproxy.com/#premium";
92const DAILY_LIMIT: &str = "You reached the maximum 50 requests for today. Get your API to make \
93                           unlimited requests at http://pubproxy.com/#premium";
94const NO_PROXY: &str = "No proxy";
95
96impl From<String> for ApiError {
97    fn from(s: String) -> Self {
98        match s.as_str() {
99            INVALID_API_KEY => Self::ApiKey,
100            RATE_LIMIT => Self::RateLimit,
101            DAILY_LIMIT => Self::DailyLimit,
102            NO_PROXY => Self::NoProxy,
103            _ => Self::Unknown,
104        }
105    }
106}