Skip to main content

context69_sdk/
error.rs

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    TaskWaitTimeout {
24        task_id: uuid::Uuid,
25        timeout: Duration,
26    },
27}
28
29impl fmt::Display for Error {
30    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
31        match self {
32            Self::InvalidBaseUrl(url) => write!(f, "invalid base url: {url}"),
33            Self::InvalidHeader(value) => write!(f, "invalid header value: {value}"),
34            Self::InvalidPersonalAccessToken(message) => write!(f, "{message}"),
35            Self::Http(error) => write!(f, "{error}"),
36            Self::Serialization(error) => write!(f, "{error}"),
37            Self::HttpStatus {
38                status,
39                api_error,
40                body,
41            } => {
42                if let Some(error) = api_error {
43                    write!(f, "http {status}: {error}")
44                } else {
45                    write!(f, "http {status}: {body}")
46                }
47            }
48            Self::AuthenticationRequired => {
49                write!(
50                    f,
51                    "personal access token is required before calling this API"
52                )
53            }
54            Self::UrlJoin { path, source } => {
55                write!(f, "failed to resolve path '{path}': {source}")
56            }
57            Self::InvalidTimeout(timeout) => {
58                write!(f, "invalid timeout: {:?}", timeout)
59            }
60            Self::TaskWaitTimeout { task_id, timeout } => {
61                write!(f, "timed out waiting for task {task_id} after {timeout:?}")
62            }
63        }
64    }
65}
66
67impl StdError for Error {
68    fn source(&self) -> Option<&(dyn StdError + 'static)> {
69        match self {
70            Self::Http(error) => Some(error),
71            Self::Serialization(error) => Some(error),
72            Self::UrlJoin { source, .. } => Some(source),
73            _ => None,
74        }
75    }
76}
77
78impl From<reqwest::Error> for Error {
79    fn from(value: reqwest::Error) -> Self {
80        Self::Http(value)
81    }
82}
83
84impl From<serde_json::Error> for Error {
85    fn from(value: serde_json::Error) -> Self {
86        Self::Serialization(value)
87    }
88}
89
90impl Error {
91    pub fn is_retryable(&self) -> bool {
92        match self {
93            Self::Http(_) => true,
94            Self::HttpStatus { status, .. } => {
95                matches!(status.as_u16(), 408 | 425 | 429 | 500..=599)
96            }
97            _ => false,
98        }
99    }
100}