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::{ProviderId, ProviderMarker, ServiceId, ServiceMarker};
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_id: ProviderId,
78 service_id: ServiceId,
79 endpoint: EndpointIdentity<'static>,
80}
81
82impl ProviderService {
83 #[must_use]
85 pub const fn new(
86 provider_id: ProviderId,
87 service_id: ServiceId,
88 endpoint: EndpointIdentity<'static>,
89 ) -> Self {
90 Self {
91 provider_id,
92 service_id,
93 endpoint,
94 }
95 }
96
97 #[must_use]
99 pub const fn from_marker<S: ServiceMarker>(endpoint: EndpointIdentity<'static>) -> Self {
100 Self::new(<S::Provider as ProviderMarker>::ID, S::ID, endpoint)
101 }
102
103 #[must_use]
105 pub const fn provider_id(self) -> ProviderId {
106 self.provider_id
107 }
108
109 #[must_use]
111 pub const fn service_id(self) -> ServiceId {
112 self.service_id
113 }
114
115 #[must_use]
117 pub const fn endpoint(self) -> EndpointIdentity<'static> {
118 self.endpoint
119 }
120}
121
122#[derive(Clone, Copy)]
124pub struct PreparedRequest<'request> {
125 request: TransportRequest<'request>,
126 service: ProviderService,
127 metadata: OperationMetadata,
128 response_policy: ResponsePolicy,
129 operation_id: Option<OperationId>,
130}
131
132impl<'request> PreparedRequest<'request> {
133 #[must_use]
135 pub const fn new(
136 request: TransportRequest<'request>,
137 service: ProviderService,
138 metadata: OperationMetadata,
139 response_policy: ResponsePolicy,
140 ) -> Self {
141 Self {
142 request,
143 service,
144 metadata,
145 response_policy,
146 operation_id: None,
147 }
148 }
149
150 #[must_use]
152 pub const fn with_operation_id(mut self, operation_id: OperationId) -> Self {
153 self.operation_id = Some(operation_id);
154 self
155 }
156
157 #[must_use]
159 pub const fn transport_request(self) -> TransportRequest<'request> {
160 self.request
161 }
162
163 #[must_use]
165 pub const fn service(self) -> ProviderService {
166 self.service
167 }
168
169 #[must_use]
171 pub const fn metadata(self) -> OperationMetadata {
172 self.metadata
173 }
174
175 #[must_use]
177 pub const fn response_policy(self) -> ResponsePolicy {
178 self.response_policy
179 }
180
181 #[must_use]
183 pub const fn operation_id(self) -> Option<OperationId> {
184 self.operation_id
185 }
186
187 pub fn validate_response<'buffer>(
189 self,
190 response: crate::transport::TransportResponse<'buffer>,
191 ) -> Result<CheckedResponse<'buffer>, ResponsePolicyError> {
192 self.response_policy.validate(response)
193 }
194
195 pub fn execute_blocking<'buffer, T>(
197 self,
198 transport: &T,
199 response_storage: &'buffer mut [u8],
200 ) -> Result<CheckedResponse<'buffer>, PreparedExecutionError<T::Error>>
201 where
202 T: BlockingTransport + BoundTransport + ResponseStorageSanitizer,
203 {
204 transport.sanitize_response_storage(response_storage);
205 self.verify_endpoint(transport)
206 .map_err(map_endpoint_error)?;
207 let admitted = self.admit_response_storage(response_storage)?;
208 let response = transport
209 .send(self.request, admitted)
210 .map_err(PreparedExecutionError::Transport)?;
211 self.response_policy
212 .validate(response)
213 .map_err(PreparedExecutionError::ResponsePolicy)
214 }
215
216 pub async fn execute_async<'transport, 'buffer, T>(
218 &'transport self,
219 transport: &'transport T,
220 response_storage: &'buffer mut [u8],
221 ) -> Result<CheckedResponse<'buffer>, PreparedExecutionError<T::Error>>
222 where
223 T: AsyncTransport + BoundTransport + ResponseStorageSanitizer,
224 'request: 'transport,
225 'buffer: 'transport,
226 {
227 transport.sanitize_response_storage(response_storage);
228 self.verify_endpoint(transport)
229 .map_err(map_endpoint_error)?;
230 let admitted = self.admit_response_storage(response_storage)?;
231 let response = transport
232 .send(self.request, admitted)
233 .await
234 .map_err(PreparedExecutionError::Transport)?;
235 self.response_policy
236 .validate(response)
237 .map_err(PreparedExecutionError::ResponsePolicy)
238 }
239
240 fn verify_endpoint<T>(self, transport: &T) -> Result<(), EndpointCheckError>
241 where
242 T: BoundTransport,
243 {
244 let actual = transport
245 .endpoint_identity()
246 .map_err(EndpointCheckError::Invalid)?;
247 if actual != self.service.endpoint {
248 return Err(EndpointCheckError::Mismatch);
249 }
250 Ok(())
251 }
252
253 fn admit_response_storage<E>(
254 self,
255 storage: &mut [u8],
256 ) -> Result<&mut [u8], PreparedExecutionError<E>> {
257 let admitted_len = core::cmp::min(storage.len(), self.response_policy.max_body_bytes());
258 storage
259 .get_mut(..admitted_len)
260 .ok_or(PreparedExecutionError::ResponseStorageUnavailable)
261 }
262}
263
264impl fmt::Debug for PreparedRequest<'_> {
265 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
266 formatter
267 .debug_struct("PreparedRequest")
268 .field("request", &self.request)
269 .field("service", &self.service)
270 .field("metadata", &self.metadata)
271 .field("response_policy", &self.response_policy)
272 .field("operation_id", &self.operation_id)
273 .finish()
274 }
275}
276
277#[derive(Clone, Copy, Eq, PartialEq)]
279pub enum PreparedExecutionError<E> {
280 EndpointIdentity(EndpointIdentityError),
282 EndpointMismatch,
284 ResponseStorageUnavailable,
286 Transport(E),
288 ResponsePolicy(ResponsePolicyError),
290}
291
292impl<E> fmt::Debug for PreparedExecutionError<E> {
293 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
294 match self {
295 Self::EndpointIdentity(error) => formatter
296 .debug_tuple("EndpointIdentity")
297 .field(error)
298 .finish(),
299 Self::EndpointMismatch => formatter.write_str("EndpointMismatch"),
300 Self::ResponseStorageUnavailable => formatter.write_str("ResponseStorageUnavailable"),
301 Self::Transport(_) => formatter.write_str("Transport([redacted])"),
302 Self::ResponsePolicy(error) => formatter
303 .debug_tuple("ResponsePolicy")
304 .field(error)
305 .finish(),
306 }
307 }
308}
309
310impl<E> fmt::Display for PreparedExecutionError<E> {
311 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
312 formatter.write_str(match self {
313 Self::EndpointIdentity(_) => "transport endpoint identity is invalid",
314 Self::EndpointMismatch => "transport endpoint differs from prepared service",
315 Self::ResponseStorageUnavailable => "response storage is unavailable",
316 Self::Transport(_) => "prepared request transport failed",
317 Self::ResponsePolicy(_) => "prepared response policy failed",
318 })
319 }
320}
321
322impl<E: fmt::Debug> core::error::Error for PreparedExecutionError<E> {}
323
324enum EndpointCheckError {
325 Invalid(EndpointIdentityError),
326 Mismatch,
327}
328
329fn map_endpoint_error<E>(error: EndpointCheckError) -> PreparedExecutionError<E> {
330 match error {
331 EndpointCheckError::Invalid(error) => PreparedExecutionError::EndpointIdentity(error),
332 EndpointCheckError::Mismatch => PreparedExecutionError::EndpointMismatch,
333 }
334}