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]
31 pub const fn new(target: &'storage mut [u8], body: &'storage mut [u8]) -> Self {
32 Self { target, body }
33 }
34
35 #[must_use]
37 pub fn into_parts(self) -> (&'storage mut [u8], &'storage mut [u8]) {
38 (self.target, self.body)
39 }
40}
41
42impl fmt::Debug for PreparationStorage<'_> {
43 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
44 formatter
45 .debug_struct("PreparationStorage")
46 .field("target_capacity", &self.target.len())
47 .field("body_capacity", &self.body.len())
48 .finish()
49 }
50}
51
52pub trait PrepareOperation {
62 type Error;
64
65 fn prepare<'storage>(
67 &self,
68 storage: PreparationStorage<'storage>,
69 ) -> Result<PreparedRequest<'storage>, Self::Error>;
70}
71
72#[derive(Clone, Copy, Debug, Eq, PartialEq)]
74pub struct ProviderService {
75 provider: Provider,
76 family: ApiFamily,
77 endpoint: EndpointIdentity<'static>,
78}
79
80impl ProviderService {
81 #[must_use]
83 pub const fn new(
84 provider: Provider,
85 family: ApiFamily,
86 endpoint: EndpointIdentity<'static>,
87 ) -> Self {
88 Self {
89 provider,
90 family,
91 endpoint,
92 }
93 }
94
95 #[must_use]
97 pub const fn provider(self) -> Provider {
98 self.provider
99 }
100
101 #[must_use]
103 pub const fn family(self) -> ApiFamily {
104 self.family
105 }
106
107 #[must_use]
109 pub const fn endpoint(self) -> EndpointIdentity<'static> {
110 self.endpoint
111 }
112}
113
114#[derive(Clone, Copy)]
116pub struct PreparedRequest<'request> {
117 request: TransportRequest<'request>,
118 service: ProviderService,
119 metadata: OperationMetadata,
120 response_policy: ResponsePolicy,
121}
122
123impl<'request> PreparedRequest<'request> {
124 #[must_use]
126 pub const fn new(
127 request: TransportRequest<'request>,
128 service: ProviderService,
129 metadata: OperationMetadata,
130 response_policy: ResponsePolicy,
131 ) -> Self {
132 Self {
133 request,
134 service,
135 metadata,
136 response_policy,
137 }
138 }
139
140 #[must_use]
142 pub const fn transport_request(self) -> TransportRequest<'request> {
143 self.request
144 }
145
146 #[must_use]
148 pub const fn service(self) -> ProviderService {
149 self.service
150 }
151
152 #[must_use]
154 pub const fn metadata(self) -> OperationMetadata {
155 self.metadata
156 }
157
158 #[must_use]
160 pub const fn response_policy(self) -> ResponsePolicy {
161 self.response_policy
162 }
163
164 pub fn execute_blocking<'buffer, T>(
166 self,
167 transport: &T,
168 response_storage: &'buffer mut [u8],
169 ) -> Result<CheckedResponse<'buffer>, PreparedExecutionError<T::Error>>
170 where
171 T: BlockingTransport + BoundTransport + ResponseStorageSanitizer,
172 {
173 transport.sanitize_response_storage(response_storage);
174 self.verify_endpoint(transport)
175 .map_err(map_endpoint_error)?;
176 let admitted = self.admit_response_storage(response_storage)?;
177 let response = transport
178 .send(self.request, admitted)
179 .map_err(PreparedExecutionError::Transport)?;
180 self.response_policy
181 .validate(response)
182 .map_err(PreparedExecutionError::ResponsePolicy)
183 }
184
185 pub async fn execute_async<'transport, 'buffer, T>(
187 &'transport self,
188 transport: &'transport T,
189 response_storage: &'buffer mut [u8],
190 ) -> Result<CheckedResponse<'buffer>, PreparedExecutionError<T::Error>>
191 where
192 T: AsyncTransport + BoundTransport + ResponseStorageSanitizer,
193 'request: 'transport,
194 'buffer: 'transport,
195 {
196 transport.sanitize_response_storage(response_storage);
197 self.verify_endpoint(transport)
198 .map_err(map_endpoint_error)?;
199 let admitted = self.admit_response_storage(response_storage)?;
200 let response = transport
201 .send(self.request, admitted)
202 .await
203 .map_err(PreparedExecutionError::Transport)?;
204 self.response_policy
205 .validate(response)
206 .map_err(PreparedExecutionError::ResponsePolicy)
207 }
208
209 fn verify_endpoint<T>(self, transport: &T) -> Result<(), EndpointCheckError>
210 where
211 T: BoundTransport,
212 {
213 let actual = transport
214 .endpoint_identity()
215 .map_err(EndpointCheckError::Invalid)?;
216 if actual != self.service.endpoint {
217 return Err(EndpointCheckError::Mismatch);
218 }
219 Ok(())
220 }
221
222 fn admit_response_storage<E>(
223 self,
224 storage: &mut [u8],
225 ) -> Result<&mut [u8], PreparedExecutionError<E>> {
226 let admitted_len = core::cmp::min(storage.len(), self.response_policy.max_body_bytes());
227 storage
228 .get_mut(..admitted_len)
229 .ok_or(PreparedExecutionError::ResponseStorageUnavailable)
230 }
231}
232
233impl fmt::Debug for PreparedRequest<'_> {
234 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
235 formatter
236 .debug_struct("PreparedRequest")
237 .field("request", &self.request)
238 .field("service", &self.service)
239 .field("metadata", &self.metadata)
240 .field("response_policy", &self.response_policy)
241 .finish()
242 }
243}
244
245#[derive(Clone, Copy, Eq, PartialEq)]
247pub enum PreparedExecutionError<E> {
248 EndpointIdentity(EndpointIdentityError),
250 EndpointMismatch,
252 ResponseStorageUnavailable,
254 Transport(E),
256 ResponsePolicy(ResponsePolicyError),
258}
259
260impl<E> fmt::Debug for PreparedExecutionError<E> {
261 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
262 match self {
263 Self::EndpointIdentity(error) => formatter
264 .debug_tuple("EndpointIdentity")
265 .field(error)
266 .finish(),
267 Self::EndpointMismatch => formatter.write_str("EndpointMismatch"),
268 Self::ResponseStorageUnavailable => formatter.write_str("ResponseStorageUnavailable"),
269 Self::Transport(_) => formatter.write_str("Transport([redacted])"),
270 Self::ResponsePolicy(error) => formatter
271 .debug_tuple("ResponsePolicy")
272 .field(error)
273 .finish(),
274 }
275 }
276}
277
278impl<E> fmt::Display for PreparedExecutionError<E> {
279 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
280 formatter.write_str(match self {
281 Self::EndpointIdentity(_) => "transport endpoint identity is invalid",
282 Self::EndpointMismatch => "transport endpoint differs from prepared service",
283 Self::ResponseStorageUnavailable => "response storage is unavailable",
284 Self::Transport(_) => "prepared request transport failed",
285 Self::ResponsePolicy(_) => "prepared response policy failed",
286 })
287 }
288}
289
290impl<E: fmt::Debug> core::error::Error for PreparedExecutionError<E> {}
291
292enum EndpointCheckError {
293 Invalid(EndpointIdentityError),
294 Mismatch,
295}
296
297fn map_endpoint_error<E>(error: EndpointCheckError) -> PreparedExecutionError<E> {
298 match error {
299 EndpointCheckError::Invalid(error) => PreparedExecutionError::EndpointIdentity(error),
300 EndpointCheckError::Mismatch => PreparedExecutionError::EndpointMismatch,
301 }
302}