1use core::fmt;
4
5use crate::operation::{CheckedResponse, OperationMetadata, ResponsePolicy, ResponsePolicyError};
6use crate::transport::{
7 AsyncTransport, BlockingTransport, BoundTransport, EndpointIdentity, EndpointIdentityError,
8 ResponseStorageSanitizer, TransportRequest,
9};
10use crate::{ApiFamily, Provider};
11
12pub struct PreparationStorage<'storage> {
14 target: &'storage mut [u8],
15 body: &'storage mut [u8],
16}
17
18impl<'storage> PreparationStorage<'storage> {
19 #[must_use]
21 pub const fn new(target: &'storage mut [u8], body: &'storage mut [u8]) -> Self {
22 Self { target, body }
23 }
24
25 #[must_use]
27 pub fn into_parts(self) -> (&'storage mut [u8], &'storage mut [u8]) {
28 (self.target, self.body)
29 }
30}
31
32impl fmt::Debug for PreparationStorage<'_> {
33 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
34 formatter
35 .debug_struct("PreparationStorage")
36 .field("target_capacity", &self.target.len())
37 .field("body_capacity", &self.body.len())
38 .finish()
39 }
40}
41
42pub trait PrepareOperation {
52 type Error;
54
55 fn prepare<'storage>(
57 &self,
58 storage: PreparationStorage<'storage>,
59 ) -> Result<PreparedRequest<'storage>, Self::Error>;
60}
61
62#[derive(Clone, Copy, Debug, Eq, PartialEq)]
64pub struct ProviderService {
65 provider: Provider,
66 family: ApiFamily,
67 endpoint: EndpointIdentity<'static>,
68}
69
70impl ProviderService {
71 #[must_use]
73 pub const fn new(
74 provider: Provider,
75 family: ApiFamily,
76 endpoint: EndpointIdentity<'static>,
77 ) -> Self {
78 Self {
79 provider,
80 family,
81 endpoint,
82 }
83 }
84
85 #[must_use]
87 pub const fn provider(self) -> Provider {
88 self.provider
89 }
90
91 #[must_use]
93 pub const fn family(self) -> ApiFamily {
94 self.family
95 }
96
97 #[must_use]
99 pub const fn endpoint(self) -> EndpointIdentity<'static> {
100 self.endpoint
101 }
102}
103
104#[derive(Clone, Copy)]
106pub struct PreparedRequest<'request> {
107 request: TransportRequest<'request>,
108 service: ProviderService,
109 metadata: OperationMetadata,
110 response_policy: ResponsePolicy,
111}
112
113impl<'request> PreparedRequest<'request> {
114 #[must_use]
116 pub const fn new(
117 request: TransportRequest<'request>,
118 service: ProviderService,
119 metadata: OperationMetadata,
120 response_policy: ResponsePolicy,
121 ) -> Self {
122 Self {
123 request,
124 service,
125 metadata,
126 response_policy,
127 }
128 }
129
130 #[must_use]
132 pub const fn transport_request(self) -> TransportRequest<'request> {
133 self.request
134 }
135
136 #[must_use]
138 pub const fn service(self) -> ProviderService {
139 self.service
140 }
141
142 #[must_use]
144 pub const fn metadata(self) -> OperationMetadata {
145 self.metadata
146 }
147
148 #[must_use]
150 pub const fn response_policy(self) -> ResponsePolicy {
151 self.response_policy
152 }
153
154 pub fn execute_blocking<'buffer, T>(
156 self,
157 transport: &T,
158 response_storage: &'buffer mut [u8],
159 ) -> Result<CheckedResponse<'buffer>, PreparedExecutionError<T::Error>>
160 where
161 T: BlockingTransport + BoundTransport + ResponseStorageSanitizer,
162 {
163 transport.sanitize_response_storage(response_storage);
164 self.verify_endpoint(transport)
165 .map_err(map_endpoint_error)?;
166 let admitted = self.admit_response_storage(response_storage)?;
167 let response = transport
168 .send(self.request, admitted)
169 .map_err(PreparedExecutionError::Transport)?;
170 self.response_policy
171 .validate(response)
172 .map_err(PreparedExecutionError::ResponsePolicy)
173 }
174
175 pub async fn execute_async<'transport, 'buffer, T>(
177 &'transport self,
178 transport: &'transport T,
179 response_storage: &'buffer mut [u8],
180 ) -> Result<CheckedResponse<'buffer>, PreparedExecutionError<T::Error>>
181 where
182 T: AsyncTransport + BoundTransport + ResponseStorageSanitizer,
183 'request: 'transport,
184 'buffer: 'transport,
185 {
186 transport.sanitize_response_storage(response_storage);
187 self.verify_endpoint(transport)
188 .map_err(map_endpoint_error)?;
189 let admitted = self.admit_response_storage(response_storage)?;
190 let response = transport
191 .send(self.request, admitted)
192 .await
193 .map_err(PreparedExecutionError::Transport)?;
194 self.response_policy
195 .validate(response)
196 .map_err(PreparedExecutionError::ResponsePolicy)
197 }
198
199 fn verify_endpoint<T>(self, transport: &T) -> Result<(), EndpointCheckError>
200 where
201 T: BoundTransport,
202 {
203 let actual = transport
204 .endpoint_identity()
205 .map_err(EndpointCheckError::Invalid)?;
206 if actual != self.service.endpoint {
207 return Err(EndpointCheckError::Mismatch);
208 }
209 Ok(())
210 }
211
212 fn admit_response_storage<E>(
213 self,
214 storage: &mut [u8],
215 ) -> Result<&mut [u8], PreparedExecutionError<E>> {
216 let admitted_len = core::cmp::min(storage.len(), self.response_policy.max_body_bytes());
217 storage
218 .get_mut(..admitted_len)
219 .ok_or(PreparedExecutionError::ResponseStorageUnavailable)
220 }
221}
222
223impl fmt::Debug for PreparedRequest<'_> {
224 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
225 formatter
226 .debug_struct("PreparedRequest")
227 .field("request", &self.request)
228 .field("service", &self.service)
229 .field("metadata", &self.metadata)
230 .field("response_policy", &self.response_policy)
231 .finish()
232 }
233}
234
235#[derive(Clone, Copy, Eq, PartialEq)]
237pub enum PreparedExecutionError<E> {
238 EndpointIdentity(EndpointIdentityError),
240 EndpointMismatch,
242 ResponseStorageUnavailable,
244 Transport(E),
246 ResponsePolicy(ResponsePolicyError),
248}
249
250impl<E> fmt::Debug for PreparedExecutionError<E> {
251 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
252 match self {
253 Self::EndpointIdentity(error) => formatter
254 .debug_tuple("EndpointIdentity")
255 .field(error)
256 .finish(),
257 Self::EndpointMismatch => formatter.write_str("EndpointMismatch"),
258 Self::ResponseStorageUnavailable => formatter.write_str("ResponseStorageUnavailable"),
259 Self::Transport(_) => formatter.write_str("Transport([redacted])"),
260 Self::ResponsePolicy(error) => formatter
261 .debug_tuple("ResponsePolicy")
262 .field(error)
263 .finish(),
264 }
265 }
266}
267
268impl<E> fmt::Display for PreparedExecutionError<E> {
269 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
270 formatter.write_str(match self {
271 Self::EndpointIdentity(_) => "transport endpoint identity is invalid",
272 Self::EndpointMismatch => "transport endpoint differs from prepared service",
273 Self::ResponseStorageUnavailable => "response storage is unavailable",
274 Self::Transport(_) => "prepared request transport failed",
275 Self::ResponsePolicy(_) => "prepared response policy failed",
276 })
277 }
278}
279
280impl<E: fmt::Debug> core::error::Error for PreparedExecutionError<E> {}
281
282enum EndpointCheckError {
283 Invalid(EndpointIdentityError),
284 Mismatch,
285}
286
287fn map_endpoint_error<E>(error: EndpointCheckError) -> PreparedExecutionError<E> {
288 match error {
289 EndpointCheckError::Invalid(error) => PreparedExecutionError::EndpointIdentity(error),
290 EndpointCheckError::Mismatch => PreparedExecutionError::EndpointMismatch,
291 }
292}