1use core::fmt;
4
5use crate::operation::{
6 CheckedResponse, OperationId, OperationMetadata, ResponsePolicy, ResponsePolicyError,
7};
8use crate::transport::{
9 AsyncTransport, BlockingTransport, BoundTransport, EndpointIdentity, EndpointIdentityError,
10 ResponseStorageSanitizer, TransportRequest,
11};
12use crate::{ApiFamily, Provider};
13
14pub struct PreparationStorage<'storage> {
16 target: &'storage mut [u8],
17 body: &'storage mut [u8],
18}
19
20impl<'storage> PreparationStorage<'storage> {
21 #[must_use]
33 pub const fn new(target: &'storage mut [u8], body: &'storage mut [u8]) -> Self {
34 Self { target, body }
35 }
36
37 #[must_use]
39 pub fn into_parts(self) -> (&'storage mut [u8], &'storage mut [u8]) {
40 (self.target, self.body)
41 }
42}
43
44impl fmt::Debug for PreparationStorage<'_> {
45 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
46 formatter
47 .debug_struct("PreparationStorage")
48 .field("target_capacity", &self.target.len())
49 .field("body_capacity", &self.body.len())
50 .finish()
51 }
52}
53
54pub trait PrepareOperation {
64 type Error;
66
67 fn prepare<'storage>(
69 &self,
70 storage: PreparationStorage<'storage>,
71 ) -> Result<PreparedRequest<'storage>, Self::Error>;
72}
73
74#[derive(Clone, Copy, Debug, Eq, PartialEq)]
76pub struct ProviderService {
77 provider: Provider,
78 family: ApiFamily,
79 endpoint: EndpointIdentity<'static>,
80}
81
82impl ProviderService {
83 #[must_use]
85 pub const fn new(
86 provider: Provider,
87 family: ApiFamily,
88 endpoint: EndpointIdentity<'static>,
89 ) -> Self {
90 Self {
91 provider,
92 family,
93 endpoint,
94 }
95 }
96
97 #[must_use]
99 pub const fn provider(self) -> Provider {
100 self.provider
101 }
102
103 #[must_use]
105 pub const fn family(self) -> ApiFamily {
106 self.family
107 }
108
109 #[must_use]
111 pub const fn endpoint(self) -> EndpointIdentity<'static> {
112 self.endpoint
113 }
114}
115
116#[derive(Clone, Copy)]
118pub struct PreparedRequest<'request> {
119 request: TransportRequest<'request>,
120 service: ProviderService,
121 metadata: OperationMetadata,
122 response_policy: ResponsePolicy,
123 operation_id: Option<OperationId>,
124}
125
126impl<'request> PreparedRequest<'request> {
127 #[must_use]
129 pub const fn new(
130 request: TransportRequest<'request>,
131 service: ProviderService,
132 metadata: OperationMetadata,
133 response_policy: ResponsePolicy,
134 ) -> Self {
135 Self {
136 request,
137 service,
138 metadata,
139 response_policy,
140 operation_id: None,
141 }
142 }
143
144 #[must_use]
146 pub const fn with_operation_id(mut self, operation_id: OperationId) -> Self {
147 self.operation_id = Some(operation_id);
148 self
149 }
150
151 #[must_use]
153 pub const fn transport_request(self) -> TransportRequest<'request> {
154 self.request
155 }
156
157 #[must_use]
159 pub const fn service(self) -> ProviderService {
160 self.service
161 }
162
163 #[must_use]
165 pub const fn metadata(self) -> OperationMetadata {
166 self.metadata
167 }
168
169 #[must_use]
171 pub const fn response_policy(self) -> ResponsePolicy {
172 self.response_policy
173 }
174
175 #[must_use]
177 pub const fn operation_id(self) -> Option<OperationId> {
178 self.operation_id
179 }
180
181 pub fn validate_response<'buffer>(
183 self,
184 response: crate::transport::TransportResponse<'buffer>,
185 ) -> Result<CheckedResponse<'buffer>, ResponsePolicyError> {
186 self.response_policy.validate(response)
187 }
188
189 pub fn execute_blocking<'buffer, T>(
191 self,
192 transport: &T,
193 response_storage: &'buffer mut [u8],
194 ) -> Result<CheckedResponse<'buffer>, PreparedExecutionError<T::Error>>
195 where
196 T: BlockingTransport + BoundTransport + ResponseStorageSanitizer,
197 {
198 transport.sanitize_response_storage(response_storage);
199 self.verify_endpoint(transport)
200 .map_err(map_endpoint_error)?;
201 let admitted = self.admit_response_storage(response_storage)?;
202 let response = transport
203 .send(self.request, admitted)
204 .map_err(PreparedExecutionError::Transport)?;
205 self.response_policy
206 .validate(response)
207 .map_err(PreparedExecutionError::ResponsePolicy)
208 }
209
210 pub async fn execute_async<'transport, 'buffer, T>(
212 &'transport self,
213 transport: &'transport T,
214 response_storage: &'buffer mut [u8],
215 ) -> Result<CheckedResponse<'buffer>, PreparedExecutionError<T::Error>>
216 where
217 T: AsyncTransport + BoundTransport + ResponseStorageSanitizer,
218 'request: 'transport,
219 'buffer: 'transport,
220 {
221 transport.sanitize_response_storage(response_storage);
222 self.verify_endpoint(transport)
223 .map_err(map_endpoint_error)?;
224 let admitted = self.admit_response_storage(response_storage)?;
225 let response = transport
226 .send(self.request, admitted)
227 .await
228 .map_err(PreparedExecutionError::Transport)?;
229 self.response_policy
230 .validate(response)
231 .map_err(PreparedExecutionError::ResponsePolicy)
232 }
233
234 fn verify_endpoint<T>(self, transport: &T) -> Result<(), EndpointCheckError>
235 where
236 T: BoundTransport,
237 {
238 let actual = transport
239 .endpoint_identity()
240 .map_err(EndpointCheckError::Invalid)?;
241 if actual != self.service.endpoint {
242 return Err(EndpointCheckError::Mismatch);
243 }
244 Ok(())
245 }
246
247 fn admit_response_storage<E>(
248 self,
249 storage: &mut [u8],
250 ) -> Result<&mut [u8], PreparedExecutionError<E>> {
251 let admitted_len = core::cmp::min(storage.len(), self.response_policy.max_body_bytes());
252 storage
253 .get_mut(..admitted_len)
254 .ok_or(PreparedExecutionError::ResponseStorageUnavailable)
255 }
256}
257
258impl fmt::Debug for PreparedRequest<'_> {
259 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
260 formatter
261 .debug_struct("PreparedRequest")
262 .field("request", &self.request)
263 .field("service", &self.service)
264 .field("metadata", &self.metadata)
265 .field("response_policy", &self.response_policy)
266 .field("operation_id", &self.operation_id)
267 .finish()
268 }
269}
270
271#[derive(Clone, Copy, Eq, PartialEq)]
273pub enum PreparedExecutionError<E> {
274 EndpointIdentity(EndpointIdentityError),
276 EndpointMismatch,
278 ResponseStorageUnavailable,
280 Transport(E),
282 ResponsePolicy(ResponsePolicyError),
284}
285
286impl<E> fmt::Debug for PreparedExecutionError<E> {
287 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
288 match self {
289 Self::EndpointIdentity(error) => formatter
290 .debug_tuple("EndpointIdentity")
291 .field(error)
292 .finish(),
293 Self::EndpointMismatch => formatter.write_str("EndpointMismatch"),
294 Self::ResponseStorageUnavailable => formatter.write_str("ResponseStorageUnavailable"),
295 Self::Transport(_) => formatter.write_str("Transport([redacted])"),
296 Self::ResponsePolicy(error) => formatter
297 .debug_tuple("ResponsePolicy")
298 .field(error)
299 .finish(),
300 }
301 }
302}
303
304impl<E> fmt::Display for PreparedExecutionError<E> {
305 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
306 formatter.write_str(match self {
307 Self::EndpointIdentity(_) => "transport endpoint identity is invalid",
308 Self::EndpointMismatch => "transport endpoint differs from prepared service",
309 Self::ResponseStorageUnavailable => "response storage is unavailable",
310 Self::Transport(_) => "prepared request transport failed",
311 Self::ResponsePolicy(_) => "prepared response policy failed",
312 })
313 }
314}
315
316impl<E: fmt::Debug> core::error::Error for PreparedExecutionError<E> {}
317
318enum EndpointCheckError {
319 Invalid(EndpointIdentityError),
320 Mismatch,
321}
322
323fn map_endpoint_error<E>(error: EndpointCheckError) -> PreparedExecutionError<E> {
324 match error {
325 EndpointCheckError::Invalid(error) => PreparedExecutionError::EndpointIdentity(error),
326 EndpointCheckError::Mismatch => PreparedExecutionError::EndpointMismatch,
327 }
328}