1use std::{error::Error as StdError, fmt, time::Duration};
2
3use reqwest::StatusCode;
4
5#[derive(Debug)]
6pub enum Error {
7 InvalidBaseUrl(String),
8 InvalidHeader(String),
9 InvalidPersonalAccessToken(String),
10 Http(reqwest::Error),
11 Serialization(serde_json::Error),
12 HttpStatus {
13 status: StatusCode,
14 api_error: Option<String>,
15 body: String,
16 },
17 AuthenticationRequired,
18 UrlJoin {
19 path: String,
20 source: url::ParseError,
21 },
22 InvalidTimeout(Duration),
23}
24
25impl fmt::Display for Error {
26 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
27 match self {
28 Self::InvalidBaseUrl(url) => write!(f, "invalid base url: {url}"),
29 Self::InvalidHeader(value) => write!(f, "invalid header value: {value}"),
30 Self::InvalidPersonalAccessToken(message) => write!(f, "{message}"),
31 Self::Http(error) => write!(f, "{error}"),
32 Self::Serialization(error) => write!(f, "{error}"),
33 Self::HttpStatus {
34 status,
35 api_error,
36 body,
37 } => {
38 if let Some(error) = api_error {
39 write!(f, "http {status}: {error}")
40 } else {
41 write!(f, "http {status}: {body}")
42 }
43 }
44 Self::AuthenticationRequired => {
45 write!(
46 f,
47 "personal access token is required before calling this API"
48 )
49 }
50 Self::UrlJoin { path, source } => {
51 write!(f, "failed to resolve path '{path}': {source}")
52 }
53 Self::InvalidTimeout(timeout) => {
54 write!(f, "invalid timeout: {:?}", timeout)
55 }
56 }
57 }
58}
59
60impl StdError for Error {
61 fn source(&self) -> Option<&(dyn StdError + 'static)> {
62 match self {
63 Self::Http(error) => Some(error),
64 Self::Serialization(error) => Some(error),
65 Self::UrlJoin { source, .. } => Some(source),
66 _ => None,
67 }
68 }
69}
70
71impl From<reqwest::Error> for Error {
72 fn from(value: reqwest::Error) -> Self {
73 Self::Http(value)
74 }
75}
76
77impl From<serde_json::Error> for Error {
78 fn from(value: serde_json::Error) -> Self {
79 Self::Serialization(value)
80 }
81}