1use core::fmt;
4
5use crate::authentication::{
6 AsyncAuthenticatedTransport, AuthenticatedRequest, AuthenticationScopePolicy,
7 BlockingAuthenticatedTransport,
8};
9use crate::operation::{
10 CheckedResponseGuard, OperationId, OperationMetadata, RequestIdPolicy, ResponsePolicy,
11 ResponsePolicyError,
12};
13use crate::transport::{
14 BoundTransport, EndpointIdentityError, EndpointPolicy, RawResponsePolicy, ResponseBuffer,
15 TransportRequest,
16};
17use crate::{ProviderId, ProviderMarker, ServiceId, ServiceMarker};
18
19#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
21pub enum BodyReplayability {
22 NotReplayable,
24 Replayable,
26}
27
28pub struct PreparationStorage<'storage> {
30 target: &'storage mut [u8],
31 body: &'storage mut [u8],
32}
33
34impl<'storage> PreparationStorage<'storage> {
35 #[must_use]
47 pub const fn new(target: &'storage mut [u8], body: &'storage mut [u8]) -> Self {
48 Self { target, body }
49 }
50
51 #[must_use]
53 pub fn into_parts(self) -> (&'storage mut [u8], &'storage mut [u8]) {
54 (self.target, self.body)
55 }
56}
57
58impl fmt::Debug for PreparationStorage<'_> {
59 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
60 formatter
61 .debug_struct("PreparationStorage")
62 .field("target_capacity", &self.target.len())
63 .field("body_capacity", &self.body.len())
64 .finish()
65 }
66}
67
68pub trait PrepareOperation {
78 type Error;
80
81 fn prepare<'storage>(
83 &self,
84 storage: PreparationStorage<'storage>,
85 ) -> Result<PreparedRequest<'storage>, Self::Error>;
86}
87
88#[derive(Clone, Copy, Debug, Eq, PartialEq)]
90pub struct ProviderService<'endpoint> {
91 provider_id: ProviderId,
92 service_id: ServiceId,
93 endpoint_policy: EndpointPolicy<'endpoint>,
94}
95
96impl<'endpoint> ProviderService<'endpoint> {
97 #[must_use]
99 pub const fn new(
100 provider_id: ProviderId,
101 service_id: ServiceId,
102 endpoint_policy: EndpointPolicy<'endpoint>,
103 ) -> Self {
104 Self {
105 provider_id,
106 service_id,
107 endpoint_policy,
108 }
109 }
110
111 #[must_use]
113 pub const fn from_marker<S: ServiceMarker>(endpoint_policy: EndpointPolicy<'endpoint>) -> Self {
114 Self::new(<S::Provider as ProviderMarker>::ID, S::ID, endpoint_policy)
115 }
116
117 #[must_use]
119 pub const fn provider_id(self) -> ProviderId {
120 self.provider_id
121 }
122
123 #[must_use]
125 pub const fn service_id(self) -> ServiceId {
126 self.service_id
127 }
128
129 #[must_use]
131 pub const fn endpoint_policy(self) -> EndpointPolicy<'endpoint> {
132 self.endpoint_policy
133 }
134}
135
136#[derive(Clone, Copy)]
138pub struct PreparedRequest<'request> {
139 request: TransportRequest<'request>,
140 service: ProviderService<'request>,
141 metadata: OperationMetadata,
142 response_policy: ResponsePolicy,
143 authentication_policy: AuthenticationScopePolicy<'request>,
144 raw_response_policy: RawResponsePolicy<'request>,
145 operation_id: Option<OperationId>,
146 body_replayability: BodyReplayability,
147}
148
149#[derive(Clone, Copy, Debug, Eq, PartialEq)]
151pub enum PreparedRequestPolicyError {
152 MissingRequestIdHeader,
154}
155
156impl fmt::Display for PreparedRequestPolicyError {
157 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
158 formatter.write_str(match self {
159 Self::MissingRequestIdHeader => {
160 "prepared request ID policy requires raw x-request-id admission"
161 }
162 })
163 }
164}
165
166impl core::error::Error for PreparedRequestPolicyError {}
167
168impl<'request> PreparedRequest<'request> {
169 pub fn new(
177 request: TransportRequest<'request>,
178 service: ProviderService<'request>,
179 metadata: OperationMetadata,
180 response_policy: ResponsePolicy,
181 authentication_policy: AuthenticationScopePolicy<'request>,
182 raw_response_policy: RawResponsePolicy<'request>,
183 ) -> Result<Self, PreparedRequestPolicyError> {
184 if metadata.request_id_policy() != RequestIdPolicy::Discard
185 && !raw_response_policy.admits_header("x-request-id")
186 {
187 return Err(PreparedRequestPolicyError::MissingRequestIdHeader);
188 }
189 Ok(Self {
190 request,
191 service,
192 metadata,
193 response_policy,
194 authentication_policy,
195 raw_response_policy,
196 operation_id: None,
197 body_replayability: if request.body().is_empty() {
198 BodyReplayability::Replayable
199 } else {
200 BodyReplayability::NotReplayable
201 },
202 })
203 }
204
205 #[must_use]
207 pub const fn with_operation_id(mut self, operation_id: OperationId) -> Self {
208 self.operation_id = Some(operation_id);
209 self
210 }
211
212 #[must_use]
217 pub const fn with_replayable_body(mut self) -> Self {
218 self.body_replayability = BodyReplayability::Replayable;
219 self
220 }
221
222 #[must_use]
224 pub const fn transport_request(self) -> TransportRequest<'request> {
225 self.request
226 }
227
228 #[must_use]
230 pub const fn service(self) -> ProviderService<'request> {
231 self.service
232 }
233
234 #[must_use]
236 pub const fn metadata(self) -> OperationMetadata {
237 self.metadata
238 }
239
240 #[must_use]
242 pub const fn response_policy(self) -> ResponsePolicy {
243 self.response_policy
244 }
245
246 #[must_use]
248 pub const fn authentication_policy(self) -> AuthenticationScopePolicy<'request> {
249 self.authentication_policy
250 }
251
252 #[must_use]
254 pub const fn raw_response_policy(self) -> RawResponsePolicy<'request> {
255 self.raw_response_policy
256 }
257
258 #[must_use]
260 pub const fn authenticated_request(self) -> AuthenticatedRequest<'request, 'request> {
261 AuthenticatedRequest::new(
262 self.request,
263 self.authentication_policy,
264 self.raw_response_policy,
265 )
266 }
267
268 #[must_use]
270 pub const fn operation_id(self) -> Option<OperationId> {
271 self.operation_id
272 }
273
274 #[must_use]
276 pub const fn body_replayability(self) -> BodyReplayability {
277 self.body_replayability
278 }
279
280 pub(crate) fn has_same_retry_policy(&self, other: &Self) -> bool {
281 self.service == other.service
282 && self.metadata == other.metadata
283 && self.response_policy == other.response_policy
284 && self.authentication_policy == other.authentication_policy
285 && self.raw_response_policy == other.raw_response_policy
286 && self.operation_id == other.operation_id
287 && self.body_replayability == other.body_replayability
288 && self.has_same_header_policy(other)
289 }
290
291 fn has_same_header_policy(&self, other: &Self) -> bool {
292 let left = self.request.headers().as_slice();
293 let right = other.request.headers().as_slice();
294 left.len() == right.len()
295 && left
296 .iter()
297 .zip(right)
298 .all(|(left, right)| left.sensitivity() == right.sensitivity())
299 }
300
301 pub fn validate_response<'buffer>(
303 self,
304 response: ResponseBuffer<'buffer>,
305 ) -> Result<CheckedResponseGuard<'buffer>, ResponsePolicyError> {
306 self.response_policy
307 .validate(response, self.metadata.request_id_policy())
308 }
309
310 pub fn apply_response_metadata_policy(
316 self,
317 response: &mut ResponseBuffer<'_>,
318 ) -> Result<(), ResponsePolicyError> {
319 super::policy::apply_request_id_policy(response, self.metadata.request_id_policy())
320 }
321
322 pub fn execute_blocking<'buffer, T>(
324 self,
325 transport: &T,
326 response_storage: &'buffer mut [u8],
327 response_header_storage: &'buffer mut [u8],
328 ) -> Result<CheckedResponseGuard<'buffer>, PreparedExecutionError<T::Error>>
329 where
330 T: BlockingAuthenticatedTransport + BoundTransport,
331 {
332 let mut response = ResponseBuffer::new(
333 response_storage,
334 self.raw_response_policy.max_body_bytes(),
335 response_header_storage,
336 );
337 self.verify_endpoint(transport)
338 .map_err(map_endpoint_error)?;
339 transport
340 .send_authenticated(self.authenticated_request(), response.writer())
341 .map_err(PreparedExecutionError::Transport)?;
342 self.response_policy
343 .validate(response, self.metadata.request_id_policy())
344 .map_err(PreparedExecutionError::ResponsePolicy)
345 }
346
347 pub async fn execute_async<'transport, 'buffer, T>(
349 &'transport self,
350 transport: &'transport T,
351 response_storage: &'buffer mut [u8],
352 response_header_storage: &'buffer mut [u8],
353 ) -> Result<CheckedResponseGuard<'buffer>, PreparedExecutionError<T::Error>>
354 where
355 T: AsyncAuthenticatedTransport + BoundTransport,
356 'request: 'transport,
357 {
358 let mut response = ResponseBuffer::new(
359 response_storage,
360 self.raw_response_policy.max_body_bytes(),
361 response_header_storage,
362 );
363 self.verify_endpoint(transport)
364 .map_err(map_endpoint_error)?;
365 transport
366 .send_authenticated(self.authenticated_request(), response.writer())
367 .await
368 .map_err(PreparedExecutionError::Transport)?;
369 self.response_policy
370 .validate(response, self.metadata.request_id_policy())
371 .map_err(PreparedExecutionError::ResponsePolicy)
372 }
373
374 fn verify_endpoint<T>(self, transport: &T) -> Result<(), EndpointCheckError>
375 where
376 T: BoundTransport,
377 {
378 let actual = transport
379 .endpoint_identity()
380 .map_err(EndpointCheckError::Invalid)?;
381 self.service
382 .endpoint_policy
383 .verify(actual)
384 .map_err(|_| EndpointCheckError::Mismatch)
385 }
386}
387
388impl fmt::Debug for PreparedRequest<'_> {
389 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
390 formatter
391 .debug_struct("PreparedRequest")
392 .field("request", &self.request)
393 .field("service", &self.service)
394 .field("metadata", &self.metadata)
395 .field("response_policy", &self.response_policy)
396 .field("authentication_policy", &self.authentication_policy)
397 .field("raw_response_policy", &self.raw_response_policy)
398 .field("operation_id", &self.operation_id)
399 .field("body_replayability", &self.body_replayability)
400 .finish()
401 }
402}
403
404#[derive(Clone, Copy, Eq, PartialEq)]
406pub enum PreparedExecutionError<E> {
407 EndpointIdentity(EndpointIdentityError),
409 EndpointMismatch,
411 Transport(E),
413 ResponsePolicy(ResponsePolicyError),
415}
416
417impl<E> fmt::Debug for PreparedExecutionError<E> {
418 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
419 match self {
420 Self::EndpointIdentity(error) => formatter
421 .debug_tuple("EndpointIdentity")
422 .field(error)
423 .finish(),
424 Self::EndpointMismatch => formatter.write_str("EndpointMismatch"),
425 Self::Transport(_) => formatter.write_str("Transport([redacted])"),
426 Self::ResponsePolicy(error) => formatter
427 .debug_tuple("ResponsePolicy")
428 .field(error)
429 .finish(),
430 }
431 }
432}
433
434impl<E> fmt::Display for PreparedExecutionError<E> {
435 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
436 formatter.write_str(match self {
437 Self::EndpointIdentity(_) => "transport endpoint identity is invalid",
438 Self::EndpointMismatch => "transport endpoint differs from prepared service",
439 Self::Transport(_) => "prepared request transport failed",
440 Self::ResponsePolicy(_) => "prepared response policy failed",
441 })
442 }
443}
444
445impl<E: fmt::Debug> core::error::Error for PreparedExecutionError<E> {}
446
447enum EndpointCheckError {
448 Invalid(EndpointIdentityError),
449 Mismatch,
450}
451
452fn map_endpoint_error<E>(error: EndpointCheckError) -> PreparedExecutionError<E> {
453 match error {
454 EndpointCheckError::Invalid(error) => PreparedExecutionError::EndpointIdentity(error),
455 EndpointCheckError::Mismatch => PreparedExecutionError::EndpointMismatch,
456 }
457}