use crate::client::Answer;
use crate::config::WireVersion;
use crate::error::{Error, UpstreamError};
use crate::outcome::{BatchOutcome, LookupOutcome, Request, TranslateOutcome, ValidateOutcome};
use http::StatusCode;
const NOT_FOUND_TX_ISSUES: [&str; 2] = ["not-found", "invalid-code"];
pub(crate) fn lookup(version: WireVersion, answer: &Answer) -> Result<LookupOutcome, Error> {
if answer.status.is_success() {
return crate::wire::lookup_found(version, &answer.body)
.map_err(|source| body(crate::LOOKUP, answer, source));
}
let upstream = UpstreamError::new(version, answer.status, answer.body.clone());
if is_not_found(&upstream) {
return Ok(LookupOutcome::NotFound {
outcome: Box::new(upstream),
});
}
Err(refusal(crate::LOOKUP, answer, upstream))
}
pub(crate) fn translate(version: WireVersion, answer: &Answer) -> Result<TranslateOutcome, Error> {
if answer.status.is_success() {
return crate::wire::translate(version, &answer.body)
.map_err(|source| body(crate::TRANSLATE, answer, source));
}
let upstream = UpstreamError::new(version, answer.status, answer.body.clone());
Err(refusal(crate::TRANSLATE, answer, upstream))
}
pub(crate) fn validate_code(
version: WireVersion,
answer: &Answer,
) -> Result<ValidateOutcome, Error> {
if answer.status.is_success() {
return crate::wire::validate_code(version, &answer.body)
.map_err(|source| body(crate::VALIDATE_CODE, answer, source));
}
let upstream = UpstreamError::new(version, answer.status, answer.body.clone());
Err(refusal(crate::VALIDATE_CODE, answer, upstream))
}
pub(crate) fn batch_entry(
version: WireVersion,
request: &Request,
answer: &Answer,
) -> Result<BatchOutcome, Error> {
match request {
Request::Lookup { .. } => lookup(version, answer).map(BatchOutcome::Lookup),
Request::Translate(_) => translate(version, answer).map(BatchOutcome::Translate),
Request::ValidateCode(_) => validate_code(version, answer).map(BatchOutcome::ValidateCode),
}
}
fn is_not_found(upstream: &UpstreamError) -> bool {
if NOT_FOUND_TX_ISSUES
.iter()
.any(|code| upstream.has_tx_issue(code))
{
return true;
}
upstream.status() == StatusCode::NOT_FOUND && upstream.outcome().is_none()
}
fn refusal(operation: &'static str, answer: &Answer, upstream: UpstreamError) -> Error {
let url = answer.url.clone();
let upstream = Box::new(upstream);
if answer.status == StatusCode::UNAUTHORIZED {
return Error::Unauthorized {
url,
challenge: None,
upstream,
};
}
if answer.status.is_server_error() {
return Error::ServerFailure { url, upstream };
}
Error::Refused {
operation,
url,
upstream,
}
}
fn body(operation: &'static str, answer: &Answer, source: crate::error::BodyError) -> Error {
Error::Body {
operation,
url: answer.url.clone(),
source: Box::new(source),
}
}