htsget_http/
error.rs

1use htsget_config::types::HtsGetError as HtsGetSearchError;
2use http::StatusCode;
3use http::header::{InvalidHeaderName, InvalidHeaderValue};
4use serde::{Deserialize, Serialize};
5use thiserror::Error;
6
7pub type Result<T> = core::result::Result<T, HtsGetError>;
8
9/// An error type that describes the errors specified in the
10/// [HtsGet specification](https://samtools.github.io/hts-specs/htsget.html)
11#[derive(Error, Debug, PartialEq, Eq)]
12pub enum HtsGetError {
13  #[error("InvalidAuthentication")]
14  InvalidAuthentication(String),
15  #[error("PermissionDenied")]
16  PermissionDenied(String),
17  #[error("NotFound")]
18  NotFound(String),
19  #[error("PayloadTooLarge")]
20  PayloadTooLarge(String),
21  #[error("UnsupportedFormat")]
22  UnsupportedFormat(String),
23  #[error("InvalidInput")]
24  InvalidInput(String),
25  #[error("InvalidRange")]
26  InvalidRange(String),
27  #[error("MethodNotAllowed")]
28  MethodNotAllowed(String),
29  #[error("InternalError")]
30  InternalError(String),
31  #[error("Wrapped")]
32  Wrapped(WrappedHtsGetError, StatusCode),
33}
34
35/// A helper struct implementing [serde's Serialize trait](Serialize) to allow
36/// easily converting HtsGetErrors to JSON
37#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq)]
38pub struct JsonHtsGetError {
39  error: String,
40  message: String,
41}
42
43/// The "htsget" container wrapping the actual error response above
44#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq)]
45pub struct WrappedHtsGetError {
46  htsget: JsonHtsGetError,
47}
48
49impl HtsGetError {
50  /// Allows converting the error to JSON and the correspondent
51  /// status code
52  pub fn to_json_representation(&self) -> (WrappedHtsGetError, StatusCode) {
53    let (err, status_code) = match self {
54      HtsGetError::InvalidAuthentication(err) => (err, StatusCode::UNAUTHORIZED),
55      HtsGetError::PermissionDenied(err) => (err, StatusCode::FORBIDDEN),
56      HtsGetError::NotFound(err) => (err, StatusCode::NOT_FOUND),
57      HtsGetError::PayloadTooLarge(err) => (err, StatusCode::PAYLOAD_TOO_LARGE),
58      HtsGetError::UnsupportedFormat(err)
59      | HtsGetError::InvalidInput(err)
60      | HtsGetError::InvalidRange(err) => (err, StatusCode::BAD_REQUEST),
61      HtsGetError::MethodNotAllowed(err) => (err, StatusCode::METHOD_NOT_ALLOWED),
62      HtsGetError::InternalError(err) => (err, StatusCode::INTERNAL_SERVER_ERROR),
63      HtsGetError::Wrapped(err, status) => return (err.clone(), *status),
64    };
65
66    (
67      WrappedHtsGetError {
68        htsget: JsonHtsGetError {
69          error: self.to_string(),
70          message: err.to_string(),
71        },
72      },
73      status_code,
74    )
75  }
76}
77
78impl From<HtsGetSearchError> for HtsGetError {
79  fn from(error: HtsGetSearchError) -> Self {
80    match error {
81      HtsGetSearchError::NotFound(err) => Self::NotFound(err),
82      HtsGetSearchError::UnsupportedFormat(err) => Self::UnsupportedFormat(err),
83      HtsGetSearchError::InvalidInput(err) => Self::InvalidInput(err),
84      HtsGetSearchError::InvalidRange(err) => Self::InvalidRange(err),
85      HtsGetSearchError::IoError(err) | HtsGetSearchError::ParseError(err) => Self::NotFound(err),
86      HtsGetSearchError::InternalError(err) => Self::InternalError(err),
87    }
88  }
89}
90
91impl From<InvalidHeaderName> for HtsGetError {
92  fn from(err: InvalidHeaderName) -> Self {
93    Self::InternalError(err.to_string())
94  }
95}
96
97impl From<InvalidHeaderValue> for HtsGetError {
98  fn from(err: InvalidHeaderValue) -> Self {
99    Self::InternalError(err.to_string())
100  }
101}
102
103impl From<reqwest_middleware::Error> for HtsGetError {
104  fn from(err: reqwest_middleware::Error) -> Self {
105    match err {
106      reqwest_middleware::Error::Middleware(err) => HtsGetError::InternalError(err.to_string()),
107      reqwest_middleware::Error::Reqwest(err) => err
108        .status()
109        .map(|status| match status {
110          StatusCode::UNAUTHORIZED => HtsGetError::InvalidAuthentication(err.to_string()),
111          StatusCode::FORBIDDEN => HtsGetError::PermissionDenied(err.to_string()),
112          StatusCode::NOT_FOUND => HtsGetError::NotFound(err.to_string()),
113          StatusCode::PAYLOAD_TOO_LARGE => HtsGetError::PayloadTooLarge(err.to_string()),
114          StatusCode::BAD_REQUEST => HtsGetError::InvalidInput(err.to_string()),
115          StatusCode::METHOD_NOT_ALLOWED => HtsGetError::MethodNotAllowed(err.to_string()),
116          _ => HtsGetError::InternalError(err.to_string()),
117        })
118        .unwrap_or(HtsGetError::InternalError(err.to_string())),
119    }
120  }
121}