1use std::string::FromUtf8Error;
2use std::time::{Duration, SystemTime};
3use std::{convert::From, error::Error, fmt, io::Error as IoError};
4
5use base64::DecodeError;
6use http::uri::InvalidUri;
7use serde_json::error::Error as JsonError;
8
9#[derive(PartialEq, Debug, Clone, Ord, PartialOrd, Eq, Deserialize, Serialize, Hash)]
10pub enum WebPushError {
11 Unspecified,
13 Unauthorized,
15 BadRequest(Option<String>),
17 ServerError(Option<Duration>),
19 NotImplemented,
21 InvalidUri,
23 EndpointNotValid,
25 EndpointNotFound,
27 PayloadTooLarge,
29 TlsError,
31 SslError,
33 IoError,
35 InvalidPackageName,
38 InvalidTtl,
40 InvalidTopic,
42 MissingCryptoKeys,
44 InvalidCryptoKeys,
46 InvalidResponse,
48 InvalidClaims,
50 Other(String),
51}
52
53impl Error for WebPushError {}
54
55impl From<JsonError> for WebPushError {
56 fn from(_: JsonError) -> WebPushError {
57 WebPushError::InvalidResponse
58 }
59}
60
61impl From<FromUtf8Error> for WebPushError {
62 fn from(_: FromUtf8Error) -> WebPushError {
63 WebPushError::InvalidResponse
64 }
65}
66
67impl From<InvalidUri> for WebPushError {
68 fn from(_: InvalidUri) -> WebPushError {
69 WebPushError::InvalidUri
70 }
71}
72
73#[cfg(feature = "hyper-client")]
74impl From<hyper::Error> for WebPushError {
75 fn from(_: hyper::Error) -> Self {
76 Self::Unspecified
77 }
78}
79
80#[cfg(feature = "isahc-client")]
81impl From<isahc::Error> for WebPushError {
82 fn from(_: isahc::Error) -> Self {
83 Self::Unspecified
84 }
85}
86
87impl From<IoError> for WebPushError {
88 fn from(_: IoError) -> WebPushError {
89 WebPushError::IoError
90 }
91}
92
93impl From<DecodeError> for WebPushError {
94 fn from(_: DecodeError) -> WebPushError {
95 WebPushError::InvalidCryptoKeys
96 }
97}
98
99impl WebPushError {
100 pub fn short_description(&self) -> &'static str {
101 match *self {
102 WebPushError::Unspecified => "unspecified",
103 WebPushError::Unauthorized => "unauthorized",
104 WebPushError::BadRequest(_) => "bad_request",
105 WebPushError::ServerError(_) => "server_error",
106 WebPushError::NotImplemented => "not_implemented",
107 WebPushError::InvalidUri => "invalid_uri",
108 WebPushError::EndpointNotValid => "endpoint_not_valid",
109 WebPushError::EndpointNotFound => "endpoint_not_found",
110 WebPushError::PayloadTooLarge => "payload_too_large",
111 WebPushError::TlsError => "tls_error",
112 WebPushError::InvalidPackageName => "invalid_package_name",
113 WebPushError::InvalidTtl => "invalid_ttl",
114 WebPushError::InvalidTopic => "invalid_topic",
115 WebPushError::InvalidResponse => "invalid_response",
116 WebPushError::MissingCryptoKeys => "missing_crypto_keys",
117 WebPushError::InvalidCryptoKeys => "invalid_crypto_keys",
118 WebPushError::SslError => "ssl_error",
119 WebPushError::IoError => "io_error",
120 WebPushError::Other(_) => "other",
121 WebPushError::InvalidClaims => "invalidClaims",
122 }
123 }
124}
125
126impl fmt::Display for WebPushError {
127 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
128 match *self {
129 WebPushError::Unspecified =>
130 write!(f, "An unknown error happened encrypting the message"),
131 WebPushError::Unauthorized =>
132 write!(f, "Please provide valid credentials to send the notification"),
133 WebPushError::BadRequest(_) =>
134 write!(f, "Request was badly formed"),
135 WebPushError::ServerError(_) =>
136 write!(f, "Server was unable to process the request, please try again later"),
137 WebPushError::PayloadTooLarge =>
138 write!(f, "Maximum allowed payload size is 3070 characters"),
139 WebPushError::InvalidUri =>
140 write!(f, "The provided URI is invalid"),
141 WebPushError::NotImplemented =>
142 write!(f, "The feature is not implemented yet"),
143 WebPushError::EndpointNotValid =>
144 write!(f, "The URL specified is no longer valid and should no longer be used"),
145 WebPushError::EndpointNotFound =>
146 write!(f, "The URL specified is invalid and should not be used again"),
147 WebPushError::TlsError =>
148 write!(f, "Could not initialize a TLS connection"),
149 WebPushError::SslError =>
150 write!(f, "Error signing with SSL"),
151 WebPushError::IoError =>
152 write!(f, "Error opening a file"),
153 WebPushError::InvalidPackageName =>
154 write!(f, "Make sure the message was addressed to a registration token whose package name matches the value passed in the request."),
155 WebPushError::InvalidTtl => write!(f, "The TTL value provided was not valid or was not provided"),
156 WebPushError::InvalidTopic => write!(f, "The Topic value provided was invalid"),
157 WebPushError::InvalidResponse => write!(f, "The response data couldn't be parses"),
158 WebPushError::MissingCryptoKeys => write!(f, "The request is missing cryptographic keys"),
159 WebPushError::InvalidCryptoKeys => write!(f, "The request is having invalid cryptographic keys"),
160 WebPushError::Other(_) => write!(f, "An unknown error when connecting the notification service"),
161 WebPushError::InvalidClaims => write!(f, "At least one JWT claim was invalid.")
162 }
163 }
164}
165
166pub struct RetryAfter;
167impl RetryAfter {
168 pub fn from_str(header_value: &str) -> Option<Duration> {
169 if let Ok(seconds) = header_value.parse::<u64>() {
170 Some(Duration::from_secs(seconds))
171 } else {
172 chrono::DateTime::parse_from_rfc2822(header_value)
173 .map(|date_time| {
174 let systime: SystemTime = date_time.into();
175
176 systime
177 .duration_since(SystemTime::now())
178 .unwrap_or_else(|_| Duration::new(0, 0))
179 })
180 .ok()
181 }
182 }
183}