1use std::{fmt, sync::Arc, time::Duration};
2
3use http::{HeaderMap, StatusCode, header::RETRY_AFTER};
4use serde::{Deserialize, Serialize};
5use serde_json::error::Category;
6use serde_json::{Map, Value};
7
8pub type Result<T> = std::result::Result<T, Error>;
9
10#[derive(Clone, Debug, Deserialize, Serialize)]
12#[non_exhaustive]
13pub struct ImportFailure {
14 #[serde(default, skip_serializing_if = "Option::is_none")]
15 pub id: Option<String>,
16 #[serde(default, skip_serializing_if = "Option::is_none")]
17 pub rule_id: Option<String>,
18 #[serde(default, skip_serializing_if = "Option::is_none")]
19 pub list_id: Option<String>,
20 #[serde(default, skip_serializing_if = "Option::is_none")]
21 pub item_id: Option<String>,
22 pub error: ImportError,
23 #[serde(flatten)]
24 pub extra: Map<String, Value>,
25}
26
27#[derive(Clone, Debug, Deserialize, Serialize)]
28#[non_exhaustive]
29pub struct ImportError {
30 pub status_code: u16,
31 pub message: String,
32 #[serde(flatten)]
33 pub extra: Map<String, Value>,
34}
35
36#[derive(thiserror::Error)]
41#[non_exhaustive]
42pub enum Error {
43 #[error("invalid client configuration: {0}")]
44 Configuration(String),
45 #[error("invalid request: {0}")]
46 InvalidRequest(String),
47 #[error("HTTP transport failed: {0}")]
48 Transport(#[source] TransportError),
49 #[error("Kibana returned HTTP {status}")]
50 #[non_exhaustive]
51 Api {
52 status: StatusCode,
53 headers: Box<HeaderMap>,
54 body: String,
56 truncated: bool,
58 body_error: Option<TransportError>,
61 },
62 #[error("reading the body of a Kibana response with HTTP {status} failed: {source}")]
66 #[non_exhaustive]
67 Body {
68 status: StatusCode,
69 headers: Box<HeaderMap>,
70 received: usize,
72 #[source]
73 source: TransportError,
74 },
75 #[error("could not decode Kibana response with HTTP {status}: {source}")]
76 #[non_exhaustive]
77 Decode {
78 status: StatusCode,
79 headers: Box<HeaderMap>,
80 body: String,
82 #[source]
83 source: DecodeError,
84 },
85 #[error("response with HTTP {status} exceeded the configured limit of {limit} bytes")]
86 #[non_exhaustive]
87 ResponseTooLarge {
88 limit: usize,
89 status: StatusCode,
90 headers: Box<HeaderMap>,
91 },
92 #[error("requested page {requested} but Kibana returned page {returned}")]
94 UnexpectedPage { requested: u32, returned: u32 },
95 #[error("stopped after {max_pages} pages before the end of the collection")]
97 PageLimit { max_pages: u32 },
98 #[error("request serialization failed: {0}")]
99 Serialize(#[source] Arc<dyn std::error::Error + Send + Sync>),
100}
101
102impl Error {
103 pub fn status(&self) -> Option<StatusCode> {
106 match self {
107 Self::Api { status, .. }
108 | Self::Body { status, .. }
109 | Self::Decode { status, .. }
110 | Self::ResponseTooLarge { status, .. } => Some(*status),
111 _ => None,
112 }
113 }
114
115 pub fn headers(&self) -> Option<&HeaderMap> {
117 match self {
118 Self::Api { headers, .. }
119 | Self::Body { headers, .. }
120 | Self::Decode { headers, .. }
121 | Self::ResponseTooLarge { headers, .. } => Some(headers),
122 _ => None,
123 }
124 }
125
126 pub fn body(&self) -> Option<&str> {
129 match self {
130 Self::Api { body, .. } | Self::Decode { body, .. } => Some(body),
131 _ => None,
132 }
133 }
134
135 pub fn transport(&self) -> Option<&TransportError> {
138 match self {
139 Self::Transport(error) | Self::Body { source: error, .. } => Some(error),
140 Self::Api { body_error, .. } => body_error.as_ref(),
141 _ => None,
142 }
143 }
144
145 pub fn message(&self) -> Option<String> {
147 let Self::Api { body, .. } = self else {
148 return None;
149 };
150 let value: serde_json::Value = serde_json::from_str(body).ok()?;
151 value.get("message")?.as_str().map(str::to_owned)
152 }
153
154 pub fn retry_after(&self) -> Option<Duration> {
157 let Self::Api { headers, .. } = self else {
158 return None;
159 };
160 let seconds = headers
161 .get(RETRY_AFTER)?
162 .to_str()
163 .ok()?
164 .trim()
165 .parse()
166 .ok()?;
167 Some(Duration::from_secs(seconds))
168 }
169
170 pub(crate) fn serialize(error: impl std::error::Error + Send + Sync + 'static) -> Self {
171 Self::Serialize(Arc::new(error))
172 }
173}
174
175impl From<reqwest::Error> for Error {
176 fn from(error: reqwest::Error) -> Self {
177 Self::Transport(TransportError::from(error))
178 }
179}
180
181impl fmt::Debug for Error {
182 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
183 let names = |headers: &HeaderMap| headers.keys().cloned().collect::<Vec<_>>();
184 match self {
185 Self::Configuration(message) => f.debug_tuple("Configuration").field(message).finish(),
186 Self::InvalidRequest(message) => {
187 f.debug_tuple("InvalidRequest").field(message).finish()
188 }
189 Self::Transport(error) => f.debug_tuple("Transport").field(error).finish(),
190 Self::Api {
191 status,
192 headers,
193 body,
194 truncated,
195 body_error,
196 } => f
197 .debug_struct("Api")
198 .field("status", status)
199 .field("headers", &names(headers))
200 .field("body_bytes", &body.len())
201 .field("truncated", truncated)
202 .field("body_error", body_error)
203 .finish(),
204 Self::Body {
205 status,
206 headers,
207 received,
208 source,
209 } => f
210 .debug_struct("Body")
211 .field("status", status)
212 .field("headers", &names(headers))
213 .field("received", received)
214 .field("source", source)
215 .finish(),
216 Self::Decode {
217 status,
218 headers,
219 body,
220 source,
221 } => f
222 .debug_struct("Decode")
223 .field("status", status)
224 .field("headers", &names(headers))
225 .field("body_bytes", &body.len())
226 .field("source", source)
227 .finish(),
228 Self::ResponseTooLarge {
229 limit,
230 status,
231 headers,
232 } => f
233 .debug_struct("ResponseTooLarge")
234 .field("limit", limit)
235 .field("status", status)
236 .field("headers", &names(headers))
237 .finish(),
238 Self::UnexpectedPage {
239 requested,
240 returned,
241 } => f
242 .debug_struct("UnexpectedPage")
243 .field("requested", requested)
244 .field("returned", returned)
245 .finish(),
246 Self::PageLimit { max_pages } => f
247 .debug_struct("PageLimit")
248 .field("max_pages", max_pages)
249 .finish(),
250 Self::Serialize(error) => f.debug_tuple("Serialize").field(error).finish(),
251 }
252 }
253}
254
255pub struct DecodeError(serde_json::Error);
262
263impl DecodeError {
264 pub(crate) fn new(error: serde_json::Error) -> Self {
265 Self(error)
266 }
267
268 pub fn category(&self) -> Category {
270 self.0.classify()
271 }
272
273 pub fn classify(&self) -> Category {
274 self.category()
275 }
276
277 pub fn line(&self) -> usize {
278 self.0.line()
279 }
280
281 pub fn column(&self) -> usize {
282 self.0.column()
283 }
284
285 pub fn inner(&self) -> &serde_json::Error {
287 &self.0
288 }
289
290 pub fn as_serde_error(&self) -> &serde_json::Error {
292 self.inner()
293 }
294}
295
296impl fmt::Display for DecodeError {
297 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
298 let category = match self.category() {
299 Category::Io => "I/O error",
300 Category::Syntax => "invalid JSON",
301 Category::Data => "unexpected JSON content",
302 Category::Eof => "unexpected end of JSON",
303 };
304 write!(
305 f,
306 "{category} at line {} column {}",
307 self.line(),
308 self.column()
309 )
310 }
311}
312
313impl fmt::Debug for DecodeError {
314 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
315 f.debug_struct("DecodeError")
316 .field("category", &self.category())
317 .field("line", &self.line())
318 .field("column", &self.column())
319 .finish()
320 }
321}
322
323impl std::error::Error for DecodeError {}
324
325impl From<reqwest::Error> for TransportError {
327 fn from(mut error: reqwest::Error) -> Self {
328 if let Some(url) = error.url_mut() {
329 url.set_query(None);
330 }
331 Self(error)
332 }
333}
334
335pub struct TransportError(reqwest::Error);
337
338impl TransportError {
339 pub fn is_timeout(&self) -> bool {
340 self.0.is_timeout()
341 }
342
343 pub fn is_connect(&self) -> bool {
345 self.0.is_connect()
346 }
347}
348
349impl fmt::Debug for TransportError {
350 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
351 fmt::Debug::fmt(&self.0, f)
352 }
353}
354
355impl fmt::Display for TransportError {
356 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
357 fmt::Display::fmt(&self.0, f)
358 }
359}
360
361impl std::error::Error for TransportError {
362 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
363 self.0.source()
364 }
365}
366
367#[derive(Clone)]
370pub(crate) enum Deferred {
371 InvalidRequest(String),
372 Serialize(Arc<dyn std::error::Error + Send + Sync>),
373}
374
375impl From<Error> for Deferred {
376 fn from(error: Error) -> Self {
377 match error {
378 Error::Serialize(source) => Self::Serialize(source),
379 Error::InvalidRequest(message) => Self::InvalidRequest(message),
380 other => Self::InvalidRequest(other.to_string()),
382 }
383 }
384}
385
386impl From<Deferred> for Error {
387 fn from(error: Deferred) -> Self {
388 match error {
389 Deferred::InvalidRequest(message) => Self::InvalidRequest(message),
390 Deferred::Serialize(source) => Self::Serialize(source),
391 }
392 }
393}