ogcapi_services/
error.rs

1use axum::{
2    Json,
3    http::{StatusCode, header::CONTENT_TYPE},
4    response::{IntoResponse, Response},
5};
6use hyper::HeaderMap;
7
8use ogcapi_types::common::{Exception, media_type::PROBLEM_JSON};
9
10/// A common error type that can be used throughout the API.
11///
12/// Can be returned in a `Result` from an API handler function.
13#[derive(thiserror::Error, Debug)]
14pub enum Error {
15    // /// Automatically return `500 Internal Server Error` on a `sqlx::Error`.
16    // #[error("an error occurred with the database")]
17    // Sqlx(#[from] sqlx::Error),
18    /// Return `404 Not Found`
19    #[error("not found")]
20    NotFound,
21
22    /// Return `500 Internal Server Error` on a `anyhow::Error`.
23    #[error("an internal server error occurred")]
24    Anyhow(#[from] anyhow::Error),
25
26    /// Return `500 Internal Server Error` on a `url::ParseError`.
27    #[error("an internal server error occurred")]
28    Url(#[from] url::ParseError),
29
30    /// Return `500 Internal Server Error` on a `serde_qs::Error`.
31    #[error("an internal server error occurred")]
32    Qs(#[from] serde_qs::Error),
33
34    /// Custom Exception
35    #[error("an ogcapi exception occurred")]
36    Exception(StatusCode, String),
37}
38
39impl Error {
40    fn status_code(&self) -> StatusCode {
41        match self {
42            Self::NotFound => StatusCode::NOT_FOUND,
43            Self::Exception(status, _) => *status,
44            _ => StatusCode::INTERNAL_SERVER_ERROR,
45        }
46    }
47}
48
49/// Axum allows you to return `Result` from handler functions, but the error type
50/// also must be some sort of response type.
51///
52/// By default, the generated `Display` impl is used to return a plaintext error message
53/// to the client.
54impl IntoResponse for Error {
55    fn into_response(self) -> Response {
56        let (status, message) = match self {
57            // Self::Sqlx(ref e) => {
58            //     tracing::error!("SQLx error: {:?}", e);
59            //     (self.status_code(), self.to_string())
60            // }
61            Self::NotFound => (self.status_code(), self.to_string()),
62            Self::Anyhow(ref e) => {
63                tracing::error!("Generic error: {:?}", e);
64                (self.status_code(), self.to_string())
65            }
66            Self::Url(ref e) => {
67                tracing::error!("Url error: {:?}", e);
68                (self.status_code(), self.to_string())
69            }
70            Self::Qs(ref e) => {
71                tracing::error!("Query string error: {:?}", e);
72                (self.status_code(), self.to_string())
73            }
74            Self::Exception(status, message) => {
75                tracing::debug!("OGCAPI exception: {}", message);
76                (status, message)
77            }
78        };
79
80        let exception = Exception::new(status.as_u16()).detail(message);
81
82        let mut headers = HeaderMap::new();
83        headers.insert(CONTENT_TYPE, PROBLEM_JSON.parse().unwrap());
84
85        (status, headers, Json(exception)).into_response()
86    }
87}