comdirect_rest_api/
error.rs1use reqwest::StatusCode;
2use thiserror::Error;
3
4pub type Result<T> = std::result::Result<T, ComdirectError>;
6
7#[derive(Debug, Error)]
9pub enum ComdirectError {
10 #[error("network error during {operation}: {source}")]
11 Network {
12 operation: &'static str,
13 #[source]
14 source: reqwest::Error,
15 },
16
17 #[error("api status {status} for {url} during {operation}")]
18 ApiStatus {
19 operation: &'static str,
20 url: String,
21 status: StatusCode,
22 body: String,
23 },
24
25 #[error("session is not authorized")]
26 NotAuthorized,
27
28 #[error("missing field '{field}' in {context}")]
29 MissingField {
30 field: &'static str,
31 context: &'static str,
32 body: String,
33 },
34
35 #[error("failed to parse JSON in {context}: {source}")]
36 JsonParse {
37 context: &'static str,
38 #[source]
39 source: serde_json::Error,
40 body: String,
41 },
42
43 #[error("invalid header '{name}': {message}")]
44 InvalidHeader { name: &'static str, message: String },
45
46 #[error("invalid input for '{field}': {message}")]
47 InvalidInput {
48 field: &'static str,
49 message: String,
50 },
51
52 #[error("login cancelled by callback")]
53 LoginCancelled,
54
55 #[error("auth flow error: {0}")]
56 AuthFlow(String),
57
58 #[error("background task '{task_name}' panicked: {message}")]
59 TaskPanicked {
60 task_name: &'static str,
61 message: String,
62 },
63}
64
65impl ComdirectError {
66 pub(crate) fn network(operation: &'static str, source: reqwest::Error) -> Self {
67 Self::Network { operation, source }
68 }
69
70 pub(crate) fn api_status(
71 operation: &'static str,
72 url: impl Into<String>,
73 status: StatusCode,
74 body: String,
75 ) -> Self {
76 Self::ApiStatus {
77 operation,
78 url: url.into(),
79 status,
80 body,
81 }
82 }
83
84 pub(crate) fn json_parse(
85 context: &'static str,
86 source: serde_json::Error,
87 body: String,
88 ) -> Self {
89 Self::JsonParse {
90 context,
91 source,
92 body,
93 }
94 }
95
96 pub(crate) fn is_transient(&self) -> bool {
97 match self {
98 Self::Network { source, .. } => {
99 source.is_timeout() || source.is_connect() || source.is_request()
100 }
101 Self::ApiStatus { status, .. } => {
102 *status == StatusCode::REQUEST_TIMEOUT
103 || *status == StatusCode::TOO_MANY_REQUESTS
104 || status.is_server_error()
105 }
106 _ => false,
107 }
108 }
109}