1use super::{
2 ClientExecutionError, ClientKernel, ClientOperation, ClientResponse, ClientResult,
3 ClientWorkspaceLease,
4};
5use crate::authentication::{
6 AsyncAuthenticatedTransport, BlockingAuthenticatedTransport, LocalAsyncAuthenticatedTransport,
7};
8use crate::diagnostics::{
9 DiagnosticContext, DiagnosticErrorCategory, DiagnosticEvent, DiagnosticObserver,
10 NoopDiagnosticObserver,
11};
12use crate::operation::{PreparationStorage, PreparedExecutionError};
13use crate::transport::BoundTransport;
14
15const NOOP_OBSERVER: NoopDiagnosticObserver = NoopDiagnosticObserver;
16
17impl<T> ClientKernel<T>
18where
19 T: BlockingAuthenticatedTransport + BoundTransport,
20{
21 pub fn execute_blocking<O, const N: usize>(
23 &self,
24 operation: &O,
25 lease: ClientWorkspaceLease<'_, '_, N>,
26 ) -> ClientResult<O::Output, O::Error, T::Error, O::DecodeError>
27 where
28 O: ClientOperation,
29 {
30 self.execute_blocking_observed(operation, lease, &NOOP_OBSERVER)
31 }
32
33 pub fn execute_blocking_observed<O, V, const N: usize>(
35 &self,
36 operation: &O,
37 mut lease: ClientWorkspaceLease<'_, '_, N>,
38 observer: &V,
39 ) -> ClientResult<O::Output, O::Error, T::Error, O::DecodeError>
40 where
41 O: ClientOperation,
42 V: DiagnosticObserver + ?Sized,
43 {
44 let mut parts = lease.parts_mut();
45 parts.clear();
46 notify(observer, DiagnosticEvent::PreparationStarted);
47 let prepared =
48 match operation.prepare(PreparationStorage::new(parts.target, parts.request_body)) {
49 Ok(prepared) => prepared,
50 Err(error) => {
51 notify(observer, preparation_failed());
52 return Err(ClientExecutionError::Preparation(error));
53 }
54 };
55 let context = DiagnosticContext::from_prepared(&prepared);
56 notify(observer, DiagnosticEvent::RequestPrepared { context });
57 notify(observer, DiagnosticEvent::DispatchStarted { context });
58 let response = match prepared.send_blocking(
59 &self.transport,
60 parts.response_body,
61 parts.response_headers,
62 ) {
63 Ok(response) => response,
64 Err(error) => {
65 notify(observer, execution_failed(context, &error));
66 return Err(ClientExecutionError::Execution(error));
67 }
68 };
69 decode_observed(operation, prepared, response, context, observer)
70 }
71}
72
73impl<T> ClientKernel<T>
74where
75 T: AsyncAuthenticatedTransport + BoundTransport + Sync,
76{
77 #[allow(clippy::manual_async_fn)]
81 pub fn execute_async<O, const N: usize>(
82 &self,
83 operation: &O,
84 lease: ClientWorkspaceLease<'_, '_, N>,
85 ) -> impl core::future::Future<
86 Output = ClientResult<O::Output, O::Error, T::Error, O::DecodeError>,
87 > + Send
88 where
89 O: ClientOperation + Sync,
90 O::Output: Send,
91 O::Error: Send,
92 O::DecodeError: Send,
93 T::Error: Send,
94 {
95 self.execute_async_observed(operation, lease, &NOOP_OBSERVER)
96 }
97
98 #[allow(clippy::manual_async_fn)]
100 pub fn execute_async_observed<O, V, const N: usize>(
101 &self,
102 operation: &O,
103 mut lease: ClientWorkspaceLease<'_, '_, N>,
104 observer: &V,
105 ) -> impl core::future::Future<
106 Output = ClientResult<O::Output, O::Error, T::Error, O::DecodeError>,
107 > + Send
108 where
109 O: ClientOperation + Sync,
110 O::Output: Send,
111 O::Error: Send,
112 O::DecodeError: Send,
113 T::Error: Send,
114 V: DiagnosticObserver + Sync + ?Sized,
115 {
116 async move {
117 let mut parts = lease.parts_mut();
118 parts.clear();
119 notify(observer, DiagnosticEvent::PreparationStarted);
120 let prepared = match operation
121 .prepare(PreparationStorage::new(parts.target, parts.request_body))
122 {
123 Ok(prepared) => prepared,
124 Err(error) => {
125 notify(observer, preparation_failed());
126 return Err(ClientExecutionError::Preparation(error));
127 }
128 };
129 let context = DiagnosticContext::from_prepared(&prepared);
130 notify(observer, DiagnosticEvent::RequestPrepared { context });
131 notify(observer, DiagnosticEvent::DispatchStarted { context });
132 let response = match prepared
133 .send_async(&self.transport, parts.response_body, parts.response_headers)
134 .await
135 {
136 Ok(response) => response,
137 Err(error) => {
138 notify(observer, execution_failed(context, &error));
139 return Err(ClientExecutionError::Execution(error));
140 }
141 };
142 decode_observed(operation, prepared, response, context, observer)
143 }
144 }
145}
146
147impl<T> ClientKernel<T>
148where
149 T: LocalAsyncAuthenticatedTransport + BoundTransport,
150{
151 pub async fn execute_local_async<O, const N: usize>(
153 &self,
154 operation: &O,
155 lease: ClientWorkspaceLease<'_, '_, N>,
156 ) -> ClientResult<O::Output, O::Error, T::Error, O::DecodeError>
157 where
158 O: ClientOperation,
159 {
160 self.execute_local_async_observed(operation, lease, &NOOP_OBSERVER)
161 .await
162 }
163
164 pub async fn execute_local_async_observed<O, V, const N: usize>(
166 &self,
167 operation: &O,
168 mut lease: ClientWorkspaceLease<'_, '_, N>,
169 observer: &V,
170 ) -> ClientResult<O::Output, O::Error, T::Error, O::DecodeError>
171 where
172 O: ClientOperation,
173 V: DiagnosticObserver + ?Sized,
174 {
175 let mut parts = lease.parts_mut();
176 parts.clear();
177 notify(observer, DiagnosticEvent::PreparationStarted);
178 let prepared =
179 match operation.prepare(PreparationStorage::new(parts.target, parts.request_body)) {
180 Ok(prepared) => prepared,
181 Err(error) => {
182 notify(observer, preparation_failed());
183 return Err(ClientExecutionError::Preparation(error));
184 }
185 };
186 let context = DiagnosticContext::from_prepared(&prepared);
187 notify(observer, DiagnosticEvent::RequestPrepared { context });
188 notify(observer, DiagnosticEvent::DispatchStarted { context });
189 let response = match prepared
190 .send_local_async(&self.transport, parts.response_body, parts.response_headers)
191 .await
192 {
193 Ok(response) => response,
194 Err(error) => {
195 notify(observer, execution_failed(context, &error));
196 return Err(ClientExecutionError::Execution(error));
197 }
198 };
199 decode_observed(operation, prepared, response, context, observer)
200 }
201}
202
203#[allow(clippy::large_types_passed_by_value)]
204fn decode_observed<O, V, T>(
205 operation: &O,
206 prepared: crate::operation::PreparedRequest<'_>,
207 response: crate::transport::ResponseBuffer<'_>,
208 context: DiagnosticContext,
209 observer: &V,
210) -> ClientResult<O::Output, O::Error, T, O::DecodeError>
211where
212 O: ClientOperation,
213 V: DiagnosticObserver + ?Sized,
214{
215 let response = ClientResponse::new(prepared, response);
216 let diagnostic = response.diagnostic_response().ok();
217 if let Some(response) = diagnostic {
218 notify(
219 observer,
220 DiagnosticEvent::ResponseReceived { context, response },
221 );
222 }
223 match operation.decode_response(response) {
224 Ok(output) => {
225 notify(
226 observer,
227 DiagnosticEvent::Completed {
228 context,
229 response: diagnostic,
230 },
231 );
232 Ok(output)
233 }
234 Err(error) => {
235 notify(
236 observer,
237 DiagnosticEvent::DecodeFailed {
238 context,
239 response: diagnostic,
240 error: DiagnosticErrorCategory::Decode,
241 },
242 );
243 Err(ClientExecutionError::Decode(error))
244 }
245 }
246}
247
248fn notify<O>(observer: &O, event: DiagnosticEvent)
249where
250 O: DiagnosticObserver + ?Sized,
251{
252 crate::diagnostics::notify(observer, event);
253}
254
255const fn preparation_failed() -> DiagnosticEvent {
256 DiagnosticEvent::PreparationFailed {
257 error: DiagnosticErrorCategory::Preparation,
258 }
259}
260
261fn execution_failed<E>(
262 context: DiagnosticContext,
263 error: &PreparedExecutionError<E>,
264) -> DiagnosticEvent {
265 let error = match error {
266 PreparedExecutionError::AuthorizationRequired
267 | PreparedExecutionError::AuthorizationInvalid(_) => DiagnosticErrorCategory::Authorization,
268 PreparedExecutionError::EndpointIdentity(_) | PreparedExecutionError::EndpointMismatch => {
269 DiagnosticErrorCategory::Endpoint
270 }
271 PreparedExecutionError::Transport(_) => DiagnosticErrorCategory::Transport,
272 PreparedExecutionError::ResponseWriter(_) => DiagnosticErrorCategory::ResponseTransaction,
273 PreparedExecutionError::ResponsePolicy(_) => DiagnosticErrorCategory::ResponsePolicy,
274 };
275 DiagnosticEvent::ExecutionFailed { context, error }
276}