#[cfg(feature = "r4b")]
use fhir_model::r4b;
#[cfg(feature = "r5")]
use fhir_model::r5;
#[cfg(feature = "stu3")]
use fhir_model::stu3;
use reqwest::StatusCode;
use thiserror::Error;
use crate::version::FhirVersion;
#[derive(Debug, Error)]
pub enum Error {
#[error("Builder is missing field `{0}` to construct the client")]
BuilderMissingField(&'static str),
#[error("Given base URL cannot be a base URL")]
UrlCannotBeBase,
#[error("Failed parsing the URL: {0}")]
UrlParse(String),
#[error("Resource is missing ID")]
MissingId,
#[error("Resource is missing version ID")]
MissingVersionId,
#[error("Missing reference URL in reference")]
MissingReference,
#[error("Tried to fetch local reference")]
LocalReference,
#[error("Was not able to clone HTTP Request")]
RequestNotClone,
#[error("URLs with mismatching origins are disabled: {0}")]
DifferentOrigin(String),
#[error("Authorization callback error: {0}")]
AuthCallback(String),
#[error("JSON error: {0}")]
Json(#[from] serde_json::Error),
#[error("Request error: {0}")]
Request(#[from] reqwest::Error),
#[error("Got error response ({0}): {1}")]
Response(StatusCode, String),
#[error("Server responded with mismatching major FHIR version: {0}")]
DifferentFhirVersion(String),
#[cfg(feature = "stu3")]
#[error("OperationOutcome({0}): {1:?}")]
OperationOutcomeStu3(StatusCode, stu3::resources::OperationOutcome),
#[cfg(feature = "r4b")]
#[error("OperationOutcome({0}): {1:?}")]
OperationOutcomeR4B(StatusCode, r4b::resources::OperationOutcome),
#[cfg(feature = "r5")]
#[error("OperationOutcome({0}): {1:?}")]
OperationOutcomeR5(StatusCode, r5::resources::OperationOutcome),
#[error("Resource `{0}` was not found")]
ResourceNotFound(String),
#[error("Failed to parse header value: {0}")]
HeaderParsing(#[from] reqwest::header::ToStrError),
#[error("Missing or wrong ETag in response: {0}")]
EtagFailure(String),
#[error("Missing or wrong Location header in response: {0}")]
LocationFailure(String),
#[error("Resource type {0} is not the requested type {1}")]
WrongResourceType(String, String),
}
impl Error {
#[must_use]
pub fn should_retry(&self) -> bool {
tracing::debug!("Checking if error `{self}` should be retried");
match self {
Self::Request(err) => err.is_connect() || err.is_request() || err.is_timeout(),
_ => false,
}
}
pub(crate) async fn from_response<V>(response: reqwest::Response) -> Self
where
V: FhirVersion,
(StatusCode, V::OperationOutcome): Into<Self>,
{
let status = response.status();
let body = response.text().await.unwrap_or_default();
if let Ok(outcome) = serde_json::from_str::<V::OperationOutcome>(&body) {
(status, outcome).into()
} else {
Self::Response(status, body)
}
}
}
#[cfg(feature = "stu3")]
impl From<(StatusCode, stu3::resources::OperationOutcome)> for Error {
fn from((status, outcome): (StatusCode, stu3::resources::OperationOutcome)) -> Self {
Self::OperationOutcomeStu3(status, outcome)
}
}
#[cfg(feature = "r4b")]
impl From<(StatusCode, r4b::resources::OperationOutcome)> for Error {
fn from((status, outcome): (StatusCode, r4b::resources::OperationOutcome)) -> Self {
Self::OperationOutcomeR4B(status, outcome)
}
}
#[cfg(feature = "r5")]
impl From<(StatusCode, r5::resources::OperationOutcome)> for Error {
fn from((status, outcome): (StatusCode, r5::resources::OperationOutcome)) -> Self {
Self::OperationOutcomeR5(status, outcome)
}
}