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
use std::time::Duration;

use crate::constants;

use thiserror::Error;
use ureq::Response;

// TODO: look into verifying the country codes that are passed in
#[derive(Error, Debug)]
pub enum ParamError {
    #[error("'{value:?}' is outside param '{param:?}' bounds: {bounds:?}")]
    OutOfBounds {
        param: String,
        bounds: (Duration, Duration),
        value: Duration,
    },
}

#[derive(Error, Debug)]
pub enum ApiError {
    #[error("Client Error ({code}): {resp}\n This should be prevented, please raise an issue")]
    ClientError { code: u16, resp: String },

    #[error("Internal Server Error ({code}): {resp}")]
    ServerError { code: u16, resp: String },

    #[error("Invalid API key, make sure your key is valid")]
    ApiKeyError,

    // TODO: mention fetchers from multiple sessions
    #[error(
        r"You have exceeded the rate limit. This could be due to multiple programs using the API.
 If this is not the case then sorry but the API hates you, consider raising an issue."
    )]
    RateLimitError,

    #[error("You have exhausted the daily limit of proxies.")]
    DailyLimitError,

    #[error("No matching proxies, consider broadening the parameters used")]
    NoProxyError,
}

impl From<Response> for ApiError {
    fn from(resp: Response) -> Self {
        let status = resp.status();
        let resp_str = resp
            .into_string()
            .expect("Failed converting response to string");

        if status >= 400 && status < 500 {
            Self::ClientError {
                code: status,
                resp: resp_str,
            }
        } else if status >= 500 && status < 600 {
            Self::ServerError {
                code: status,
                resp: resp_str,
            }
        } else {
            unreachable!(
                "Tried creating ApiError from valid response ({}). Please raise an issue at {}.",
                status,
                constants::REPO_URI
            );
        }
    }
}

const INVALID_API_KEY: &str =
    "Invalid API. Get your API to make unlimited requests at http://pubproxy.com/#premium";
const RATE_LIMIT: &str = r"We have to temporarily stop you. You're requesting proxies a little too
 fast (2+ requests per second). Get your API to remove this limit at http://pubproxy.com/#premium";
const DAILY_LIMIT: &str = r"You have reached the maximum 50 requests for today. Get your API to make
 unlimited requests at http://pubproxy.com/#premium";
const NO_PROXY: &str = "No proxy";

impl From<String> for ApiError {
    fn from(s: String) -> Self {
        match s.as_str() {
            INVALID_API_KEY => Self::ApiKeyError,
            RATE_LIMIT => Self::RateLimitError,
            DAILY_LIMIT => Self::DailyLimitError,
            NO_PROXY => Self::NoProxyError,
            _ => {
                unreachable!(
                    "The API returned an unexpected message '{}'. Please raise an issue at {}",
                    s,
                    constants::REPO_URI
                );
            }
        }
    }
}