Skip to main content

cloud_sdk/
transport.rs

1//! Provider-neutral blocking and asynchronous transport contracts.
2
3mod asynchronous;
4mod cleanup;
5mod content_type;
6mod endpoint;
7mod header;
8mod request_target;
9mod response;
10mod retained;
11mod workspace;
12
13pub use asynchronous::AsyncTransport;
14pub use cleanup::ResponseStorageSanitizer;
15pub use content_type::{
16    ContentType, ContentTypeError, MAX_CONTENT_TYPE_BYTES, MediaType, ResponseContentType,
17};
18pub use endpoint::{
19    AcknowledgedCustomEndpoint, BoundTransport, CustomEndpointAcknowledgement, EndpointIdentity,
20    EndpointIdentityError, EndpointPolicy, EndpointPolicyError, EndpointPolicyKind, EndpointScheme,
21    MAX_ENDPOINT_BASE_PATH_BYTES, MAX_ENDPOINT_HOST_BYTES, MAX_ENDPOINT_REGION_BYTES,
22    MAX_OFFICIAL_ENDPOINTS, RegionEndpoint,
23};
24pub use header::{
25    HeaderError, HeaderName, HeaderSensitivity, HeaderValue, MAX_HEADER_NAME_BYTES,
26    MAX_HEADER_VALUE_BYTES, MAX_REQUEST_HEADER_BYTES, MAX_REQUEST_HEADERS,
27    MAX_RESPONSE_HEADER_BYTES, MAX_RESPONSE_HEADERS, RequestHeader, RequestHeaders, ResponseHeader,
28    ResponseHeaders,
29};
30pub use request_target::{
31    CanonicalQuery, FormQuery, MAX_REQUEST_TARGET_BYTES, QueryPair, QueryPairs, RequestPath,
32    RequestPathError, RequestQuery, RequestTarget, RequestTargetError, StructuredQueryError,
33};
34pub use response::{
35    ResponseBuffer, ResponseMetadata, ResponseWriter, ResponseWriterError, TransportResponse,
36};
37pub use retained::{MAX_REQUEST_ID_BYTES, RetainedMetadataError, RetainedResponseMetadata};
38pub use workspace::{
39    RESPONSE_CURSOR_SCRATCH_BYTES, RESPONSE_DECODER_SCRATCH_BYTES,
40    RESPONSE_PROVIDER_LINK_SCRATCH_BYTES, ResponseDecodeWorkspace,
41};
42
43use core::fmt;
44
45use crate::Method;
46
47/// Provider-neutral request passed to a blocking transport.
48#[derive(Clone, Copy)]
49pub struct TransportRequest<'a> {
50    method: Method,
51    target: RequestTarget<'a>,
52    body: &'a [u8],
53    headers: RequestHeaders<'a>,
54}
55
56impl<'a> TransportRequest<'a> {
57    /// Creates a bodyless request.
58    #[must_use]
59    pub const fn new(method: Method, target: RequestTarget<'a>) -> Self {
60        Self {
61            method,
62            target,
63            body: &[],
64            headers: RequestHeaders::EMPTY,
65        }
66    }
67
68    /// Adds a borrowed request body.
69    #[must_use]
70    pub const fn with_body(mut self, body: &'a [u8]) -> Self {
71        self.body = body;
72        self
73    }
74
75    /// Adds a complete validated request-header block.
76    #[must_use]
77    pub const fn with_headers(mut self, headers: RequestHeaders<'a>) -> Self {
78        self.headers = headers;
79        self
80    }
81
82    /// Returns the HTTP method.
83    #[must_use]
84    pub const fn method(self) -> Method {
85        self.method
86    }
87
88    /// Returns the validated origin-form target.
89    #[must_use]
90    pub const fn target(self) -> RequestTarget<'a> {
91        self.target
92    }
93
94    /// Returns the borrowed body bytes.
95    #[must_use]
96    pub const fn body(self) -> &'a [u8] {
97        self.body
98    }
99
100    /// Returns the complete ordered request-header block.
101    #[must_use]
102    pub const fn headers(self) -> RequestHeaders<'a> {
103        self.headers
104    }
105}
106
107impl fmt::Debug for TransportRequest<'_> {
108    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
109        formatter
110            .debug_struct("TransportRequest")
111            .field("method", &self.method)
112            .field("target", &self.target)
113            .field("body", &"[redacted]")
114            .field("headers", &self.headers)
115            .finish()
116    }
117}
118
119/// Valid HTTP response status code.
120#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
121pub struct StatusCode(u16);
122
123impl StatusCode {
124    /// `200 OK`.
125    pub const OK: Self = Self(200);
126    /// `201 Created`.
127    pub const CREATED: Self = Self(201);
128    /// `202 Accepted`.
129    pub const ACCEPTED: Self = Self(202);
130    /// `204 No Content`.
131    pub const NO_CONTENT: Self = Self(204);
132    /// `429 Too Many Requests`.
133    pub const TOO_MANY_REQUESTS: Self = Self(429);
134
135    /// Creates a status code in the HTTP `100..=599` range.
136    #[must_use]
137    pub const fn new(value: u16) -> Option<Self> {
138        if value < 100 || value > 599 {
139            return None;
140        }
141        Some(Self(value))
142    }
143
144    /// Returns the numeric status code.
145    #[must_use]
146    pub const fn get(self) -> u16 {
147        self.0
148    }
149
150    /// Reports whether this is a success status.
151    #[must_use]
152    pub const fn is_success(self) -> bool {
153        self.0 >= 200 && self.0 <= 299
154    }
155
156    /// Reports whether this is a client or server error status.
157    #[must_use]
158    pub const fn is_error(self) -> bool {
159        self.0 >= 400
160    }
161}
162
163/// Synchronous transport over caller-owned request and response buffers.
164///
165/// Authentication, base URLs, headers, timeouts, TLS, and retry policy belong
166/// to adapters and are intentionally outside this minimal contract.
167/// The shared receiver does not itself promise concurrency: callers may issue
168/// overlapping requests only when the concrete implementation satisfies their
169/// required [`Sync`] and [`Send`] bounds. Sequential implementations may use
170/// safe interior mutability without becoming `Sync`.
171pub trait BlockingTransport {
172    /// Transport-specific failure.
173    type Error;
174
175    /// Sends one request and writes the response body into the caller buffer.
176    fn send(
177        &self,
178        request: TransportRequest<'_>,
179        response: &mut ResponseWriter<'_>,
180    ) -> Result<(), Self::Error>;
181}
182
183#[cfg(test)]
184mod tests;