use http::StatusCode;
use crate::authorizer::Unsent;
use crate::outcome::IssueType;
#[derive(Debug, Clone, Copy, PartialEq, Eq, thiserror::Error)]
#[non_exhaustive]
pub enum InvalidInput {
#[error("an identifier system is not an absolute URI")]
System,
#[error("a search value is empty")]
EmptyValue,
#[error("an _id value is not a FHIR id")]
Id,
#[error("a birthdate value is not a FHIR date")]
Date,
#[error("the Supplier base URL is not an http(s) URL without a query or a fragment")]
Base,
}
#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
pub enum PdqmError {
#[error("the Supplier does not recognise an identifier domain the query names")]
DomainNotRecognized,
#[error("the Supplier answered {status}")]
Rejected {
status: StatusCode,
issues: Vec<IssueType>,
},
#[error("the page link points away from the Supplier")]
ForeignPage,
#[error("the Supplier did not answer within the timeout")]
Timeout,
#[error("the Supplier could not be reached")]
Transport(#[source] reqwest::Error),
#[error("the request to the Supplier could not be authenticated")]
Unauthenticated(#[source] crate::authorizer::AuthorizerError),
#[error("the Supplier's answer does not hold to the transaction")]
Malformed(#[from] Malformation),
#[error("the request to the Supplier could not be written")]
Unwritable(#[source] serde_json::Error),
#[cfg(feature = "balp")]
#[error("the PDQm audit record could not be recorded")]
Audit(#[source] ihe_atna::balp::AuditError),
}
#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
#[non_exhaustive]
pub enum Malformation {
#[error("the answer is not application/fhir+json")]
NotFhirJson,
#[error("the answer exceeds {limit} bytes")]
TooLarge {
limit: usize,
},
#[error("the answer is not JSON (line {line}, column {column})")]
NotJson {
line: usize,
column: usize,
},
#[error("the answer is not a FHIR resource")]
NotAResource,
#[error("the answer is not a Bundle")]
UnexpectedResource,
#[error("the resource does not decode as FHIR R4: {kind}")]
Decode {
kind: fhir_types::codec::DecodeErrorKind,
},
#[error("the Bundle is not a searchset")]
NotSearchset,
#[error("the Bundle has no total")]
NoTotal,
#[error("the Bundle holds more matches than its total")]
TotalBelowMatches,
#[error("entry {index} has no fullUrl")]
NoFullUrl {
index: usize,
},
#[error("entry {index} has no resource")]
NoResource {
index: usize,
},
#[error("entry {index} holds a resource ITI-78 does not answer with")]
UnexpectedEntry {
index: usize,
},
#[error("entry {index} has a score that is not a finite decimal")]
Score {
index: usize,
},
#[error("entry {index} has a match-grade outside its value set")]
MatchGrade {
index: usize,
},
#[error("the next link is not a URL")]
NextLink,
#[error("entry {index} is not in search mode match")]
NotMatchMode {
index: usize,
},
#[error("entry {index} has no score between 0 and 1")]
NoScore {
index: usize,
},
#[error("entry {index} has no match-grade")]
NoMatchGrade {
index: usize,
},
#[error("entry {index} is an error outcome in a successful answer")]
ErrorOutcome {
index: usize,
},
}
pub(super) fn transport(error: reqwest::Error) -> PdqmError {
let error = error.without_url();
if error.is_timeout() {
PdqmError::Timeout
} else {
PdqmError::Transport(error)
}
}
pub(super) fn unsent(error: Unsent) -> PdqmError {
match error {
Unsent::Unauthenticated(source) => PdqmError::Unauthenticated(source),
Unsent::Timeout => PdqmError::Timeout,
Unsent::Transport(source) => transport(source),
}
}