use core::fmt;
use crate::diagnostics::{DiagnosticRequestId, DiagnosticResponse};
use crate::operation::{CheckedResponse, PreparedRequest, ResponsePolicyError};
use crate::transport::{
ResponseBuffer, ResponseDecodeWorkspace, ResponseWriterError, StatusCode, TransportResponse,
};
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum ClientResponseKind {
Success,
Error,
Other,
}
pub enum CheckedDecodeError<E> {
ResponseWriter(ResponseWriterError),
ResponsePolicy(ResponsePolicyError),
ExpectedErrorStatus,
Decoder(E),
}
impl<E> fmt::Debug for CheckedDecodeError<E> {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str(match self {
Self::ResponseWriter(_) => "CheckedDecodeError::ResponseWriter",
Self::ResponsePolicy(_) => "CheckedDecodeError::ResponsePolicy",
Self::ExpectedErrorStatus => "CheckedDecodeError::ExpectedErrorStatus",
Self::Decoder(_) => "CheckedDecodeError::Decoder([redacted])",
})
}
}
impl<E> fmt::Display for CheckedDecodeError<E> {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str(match self {
Self::ResponseWriter(_) => "client response is not committed",
Self::ResponsePolicy(_) => "client response policy rejected the response",
Self::ExpectedErrorStatus => "client error decoder requires an error status",
Self::Decoder(_) => "provider response decoder failed",
})
}
}
impl<E> core::error::Error for CheckedDecodeError<E> {}
pub struct ClientResponse<'request, 'buffer> {
prepared: PreparedRequest<'request>,
response: ResponseBuffer<'buffer>,
}
impl<'request, 'buffer> ClientResponse<'request, 'buffer> {
#[allow(clippy::large_types_passed_by_value)]
pub(crate) const fn new(
prepared: PreparedRequest<'request>,
response: ResponseBuffer<'buffer>,
) -> Self {
Self { prepared, response }
}
pub fn status(&self) -> Result<StatusCode, ResponseWriterError> {
self.response.with_response(|response| response.status())
}
pub fn kind(&self) -> Result<ClientResponseKind, ResponseWriterError> {
self.status().map(|status| {
if status.is_success() {
ClientResponseKind::Success
} else if status.is_error() {
ClientResponseKind::Error
} else {
ClientResponseKind::Other
}
})
}
pub(crate) fn diagnostic_response(&self) -> Result<DiagnosticResponse, ResponseWriterError> {
let status = self.status()?;
let policy = self.prepared.metadata().request_id_policy();
let present = match policy {
crate::operation::RequestIdPolicy::Discard => false,
crate::operation::RequestIdPolicy::Protected
| crate::operation::RequestIdPolicy::Retain => self.response.has_request_id()?,
};
let request_id = DiagnosticRequestId::classify(policy, present);
Ok(DiagnosticResponse::new(status, request_id))
}
pub fn decode_success_owned<R, E>(
self,
decode: impl for<'response> FnOnce(
CheckedResponse<'response>,
&mut ResponseDecodeWorkspace,
) -> Result<R, E>,
) -> Result<R, CheckedDecodeError<E>> {
let checked = self
.prepared
.validate_response(self.response)
.map_err(CheckedDecodeError::ResponsePolicy)?;
checked
.decode_owned_with_workspace(decode)
.map_err(CheckedDecodeError::Decoder)
}
pub fn decode_error_owned<R, E>(
mut self,
decode: impl for<'response> FnOnce(
TransportResponse<'response, 'buffer>,
&mut ResponseDecodeWorkspace,
) -> Result<R, E>,
) -> Result<R, CheckedDecodeError<E>> {
let status = self.status().map_err(CheckedDecodeError::ResponseWriter)?;
if !status.is_error() {
return Err(CheckedDecodeError::ExpectedErrorStatus);
}
self.prepared
.apply_response_metadata_policy(&mut self.response)
.map_err(CheckedDecodeError::ResponsePolicy)?;
let mut workspace = ResponseDecodeWorkspace::new_for_provider();
self.response
.with_response(|response| decode(response, &mut workspace))
.map_err(CheckedDecodeError::ResponseWriter)?
.map_err(CheckedDecodeError::Decoder)
}
}
impl fmt::Debug for ClientResponse<'_, '_> {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter
.debug_struct("ClientResponse")
.field("prepared", &"[bound]")
.field("response", &"[redacted]")
.finish()
}
}