open_payments/client/
error.rs1use std::collections::HashMap;
42use thiserror::Error;
43
44#[derive(Debug, Error)]
54pub struct OpClientError {
55 pub description: String,
57
58 pub validation_errors: Option<Vec<String>>,
60
61 pub status: Option<String>,
63
64 pub code: Option<u16>,
66
67 pub details: Option<HashMap<String, serde_json::Value>>,
69}
70
71impl OpClientError {
72 pub fn http(description: impl Into<String>, status: Option<String>, code: Option<u16>) -> Self {
74 Self {
75 description: description.into(),
76 validation_errors: None,
77 status,
78 code,
79 details: None,
80 }
81 }
82
83 pub fn validation(description: impl Into<String>, validation_errors: Vec<String>) -> Self {
85 Self {
86 description: description.into(),
87 validation_errors: Some(validation_errors),
88 status: None,
89 code: None,
90 details: None,
91 }
92 }
93
94 pub fn other(description: impl Into<String>) -> Self {
96 Self {
97 description: description.into(),
98 validation_errors: None,
99 status: None,
100 code: None,
101 details: None,
102 }
103 }
104
105 pub fn with_details(mut self, details: HashMap<String, serde_json::Value>) -> Self {
107 self.details = Some(details);
108 self
109 }
110
111 pub fn with_detail(mut self, key: impl Into<String>, value: serde_json::Value) -> Self {
113 if self.details.is_none() {
114 self.details = Some(HashMap::new());
115 }
116 if let Some(ref mut details) = self.details {
117 details.insert(key.into(), value);
118 }
119 self
120 }
121}
122
123impl std::fmt::Display for OpClientError {
124 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
125 write!(f, "{}", self.description)?;
126
127 if let Some(status) = &self.status {
128 write!(f, " (Status: {status})")?;
129 }
130
131 if let Some(code) = &self.code {
132 write!(f, " (Code: {code})")?;
133 }
134
135 if let Some(validation_errors) = &self.validation_errors {
136 write!(f, " [Validation errors: {}]", validation_errors.join(", "))?;
137 }
138
139 Ok(())
140 }
141}
142
143impl From<reqwest::Error> for OpClientError {
144 fn from(err: reqwest::Error) -> Self {
145 let status = err
146 .status()
147 .map(|s| s.canonical_reason().unwrap_or("Unknown").to_string());
148 let code = err.status().map(|s| s.as_u16());
149 let description = format!("HTTP error: {err}");
150
151 Self {
152 description,
153 validation_errors: None,
154 status,
155 code,
156 details: None,
157 }
158 }
159}
160
161impl From<serde_json::Error> for OpClientError {
162 fn from(err: serde_json::Error) -> Self {
163 Self::other(format!("JSON serialization/deserialization error: {err}"))
164 }
165}
166
167impl From<std::io::Error> for OpClientError {
168 fn from(err: std::io::Error) -> Self {
169 Self::other(format!("I/O error: {err}"))
170 }
171}
172
173impl From<base64::DecodeError> for OpClientError {
174 fn from(err: base64::DecodeError) -> Self {
175 Self::other(format!("Base64 decoding error: {err}"))
176 }
177}
178
179impl From<url::ParseError> for OpClientError {
180 fn from(err: url::ParseError) -> Self {
181 Self::other(format!("URL parsing error: {err}"))
182 }
183}
184
185impl OpClientError {
186 pub fn header_parse(description: impl Into<String>) -> Self {
187 Self::other(format!("Header parse error: {}", description.into()))
188 }
189
190 pub fn pem(description: impl Into<String>) -> Self {
191 Self::other(format!("Invalid PEM: {}", description.into()))
192 }
193
194 pub fn pkcs8(description: impl Into<String>) -> Self {
195 Self::other(format!("PKCS8 error: {}", description.into()))
196 }
197
198 pub fn signature(description: impl Into<String>) -> Self {
199 Self::other(format!("Signature error: {}", description.into()))
200 }
201}
202
203impl From<url::ParseError> for Box<OpClientError> {
204 fn from(err: url::ParseError) -> Self {
205 Box::new(OpClientError::from(err))
206 }
207}
208
209impl From<reqwest::Error> for Box<OpClientError> {
210 fn from(err: reqwest::Error) -> Self {
211 Box::new(OpClientError::from(err))
212 }
213}
214
215impl From<serde_json::Error> for Box<OpClientError> {
216 fn from(err: serde_json::Error) -> Self {
217 Box::new(OpClientError::from(err))
218 }
219}
220
221impl From<std::io::Error> for Box<OpClientError> {
222 fn from(err: std::io::Error) -> Self {
223 Box::new(OpClientError::from(err))
224 }
225}
226
227impl From<base64::DecodeError> for Box<OpClientError> {
228 fn from(err: base64::DecodeError) -> Self {
229 Box::new(OpClientError::from(err))
230 }
231}
232
233pub type Result<T> = std::result::Result<T, Box<OpClientError>>;