cloud_sdk/client/
response.rs1use core::fmt;
2
3use crate::diagnostics::{DiagnosticRequestId, DiagnosticResponse};
4use crate::operation::CheckedResponseGuard;
5use crate::operation::{CheckedResponse, PreparedRequest, ResponsePolicyError};
6use crate::transport::{
7 ResponseBuffer, ResponseDecodeWorkspace, ResponseWriterError, StatusCode, TransportResponse,
8};
9
10#[derive(Clone, Copy, Debug, Eq, PartialEq)]
12pub enum ClientResponseKind {
13 Success,
15 Error,
17 Other,
19}
20
21pub enum CheckedDecodeError<E> {
23 ResponseWriter(ResponseWriterError),
25 ResponsePolicy(ResponsePolicyError),
27 ExpectedErrorStatus,
29 Decoder(E),
31}
32
33impl<E> fmt::Debug for CheckedDecodeError<E> {
34 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
35 formatter.write_str(match self {
36 Self::ResponseWriter(_) => "CheckedDecodeError::ResponseWriter",
37 Self::ResponsePolicy(_) => "CheckedDecodeError::ResponsePolicy",
38 Self::ExpectedErrorStatus => "CheckedDecodeError::ExpectedErrorStatus",
39 Self::Decoder(_) => "CheckedDecodeError::Decoder([redacted])",
40 })
41 }
42}
43
44impl<E> fmt::Display for CheckedDecodeError<E> {
45 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
46 formatter.write_str(match self {
47 Self::ResponseWriter(_) => "client response is not committed",
48 Self::ResponsePolicy(_) => "client response policy rejected the response",
49 Self::ExpectedErrorStatus => "client error decoder requires an error status",
50 Self::Decoder(_) => "provider response decoder failed",
51 })
52 }
53}
54
55impl<E> core::error::Error for CheckedDecodeError<E> {}
56
57pub struct ClientResponse<'request, 'buffer> {
63 prepared: PreparedRequest<'request>,
64 response: ResponseBuffer<'buffer>,
65}
66
67impl<'request, 'buffer> ClientResponse<'request, 'buffer> {
68 #[allow(clippy::large_types_passed_by_value)]
69 pub(crate) const fn new(
70 prepared: PreparedRequest<'request>,
71 response: ResponseBuffer<'buffer>,
72 ) -> Self {
73 Self { prepared, response }
74 }
75
76 pub fn status(&self) -> Result<StatusCode, ResponseWriterError> {
78 self.response.with_response(|response| response.status())
79 }
80
81 pub fn kind(&self) -> Result<ClientResponseKind, ResponseWriterError> {
83 self.status().map(|status| {
84 if status.is_success() {
85 ClientResponseKind::Success
86 } else if status.is_error() {
87 ClientResponseKind::Error
88 } else {
89 ClientResponseKind::Other
90 }
91 })
92 }
93
94 pub(crate) fn diagnostic_response(&self) -> Result<DiagnosticResponse, ResponseWriterError> {
95 let status = self.status()?;
96 let policy = self.prepared.metadata().request_id_policy();
97 let present = match policy {
98 crate::operation::RequestIdPolicy::Discard => false,
99 crate::operation::RequestIdPolicy::Protected
100 | crate::operation::RequestIdPolicy::Retain => self.response.has_request_id()?,
101 };
102 let request_id = DiagnosticRequestId::classify(policy, present);
103 Ok(DiagnosticResponse::new(status, request_id))
104 }
105
106 pub fn decode_success_owned<R, E>(
108 self,
109 decode: impl for<'response> FnOnce(
110 CheckedResponse<'response>,
111 &mut ResponseDecodeWorkspace,
112 ) -> Result<R, E>,
113 ) -> Result<R, CheckedDecodeError<E>> {
114 let checked = self
115 .prepared
116 .validate_response(self.response)
117 .map_err(CheckedDecodeError::ResponsePolicy)?;
118 checked
119 .decode_owned_with_workspace(decode)
120 .map_err(CheckedDecodeError::Decoder)
121 }
122
123 pub fn decode_success_guarded<R, E>(
129 self,
130 decode: impl FnOnce(CheckedResponseGuard<'buffer>) -> Result<R, E>,
131 ) -> Result<R, CheckedDecodeError<E>> {
132 let checked = self
133 .prepared
134 .validate_response(self.response)
135 .map_err(CheckedDecodeError::ResponsePolicy)?;
136 decode(checked).map_err(CheckedDecodeError::Decoder)
137 }
138
139 pub fn decode_error_owned<R, E>(
141 mut self,
142 decode: impl for<'response> FnOnce(
143 TransportResponse<'response, 'buffer>,
144 &mut ResponseDecodeWorkspace,
145 ) -> Result<R, E>,
146 ) -> Result<R, CheckedDecodeError<E>> {
147 let status = self.status().map_err(CheckedDecodeError::ResponseWriter)?;
148 if !status.is_error() {
149 return Err(CheckedDecodeError::ExpectedErrorStatus);
150 }
151 self.prepared
152 .apply_response_metadata_policy(&mut self.response)
153 .map_err(CheckedDecodeError::ResponsePolicy)?;
154 let mut workspace = ResponseDecodeWorkspace::new_for_provider();
155 self.response
156 .with_response(|response| decode(response, &mut workspace))
157 .map_err(CheckedDecodeError::ResponseWriter)?
158 .map_err(CheckedDecodeError::Decoder)
159 }
160}
161
162impl fmt::Debug for ClientResponse<'_, '_> {
163 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
164 formatter
165 .debug_struct("ClientResponse")
166 .field("prepared", &"[bound]")
167 .field("response", &"[redacted]")
168 .finish()
169 }
170}