omnia_sdk/api/request.rs
1//! # Request Handler API
2//!
3//! This module provides a type-safe API for handling requests using the
4//! [typestate pattern](https://cliffle.com/blog/rust-typestate/).
5//!
6//! ## Core Types
7//!
8//! - [`Handler`]: Trait implemented by request types to process requests and produce [`Reply`]
9//! - [`RequestHandler`]: Type-state builder for configuring and executing requests
10//! - [`Context`]: Request-scoped context passed to handlers
11//!
12//! ## Typestate Pattern
13//!
14//! The type system ensures requests are properly configured at compile time,
15//! preventing execution without required components (owner, provider, request).
16//!
17//! ### Valid State Transitions
18//!
19//! ```text
20//! RequestHandler<NoRequest, NoOwner, NoProvider>
21//! → .owner("alice") → RequestHandler<NoRequest, OwnerSet, NoProvider>
22//! → .provider(p) → RequestHandler<NoRequest, OwnerSet, ProviderSet<P>>
23//! → .request(r) → RequestHandler<RequestSet<R,P>, OwnerSet, ProviderSet<P>>
24//! → .handle().await → Result<Reply<R::Output>, R::Error>
25//! ```
26//!
27//! Methods can be called in any order (except `.handle()` which must be last).
28//! The `.headers()` method can be called at any point in the chain.
29//!
30//! ## Usage Examples
31//!
32//! ### Example: From raw input (Recommended)
33//!
34//! The [`Handler::handler()`] method provides a convenient API for a 'oneshot'
35//! builder pattern:
36//!
37//! ```rust,ignore
38//!
39//! // Simple request - await directly (IntoFuture)
40//! let response = MyRequest::handler(bytes)?
41//! .owner("alice")
42//! .provider(my_provider)
43//! .await?;
44//!
45//! // Or explicitly call `handle()`
46//! let response = Request::handler(body.to_vec())?
47//! .provider(my_provider)
48//! .owner("owner")
49//! .handle()
50//! .await?;
51//! ```
52//!
53//! ### Example: Using [`RequestHandler`] Directly
54//!
55//! ```rust,ignore
56//! use omnia_sdk::api::{RequestHandler, Handler};
57//!
58//! // Manual construction with typestate safety
59//! let response = RequestHandler::new()
60//! .owner("alice")
61//! .provider(my_provider)
62//! .request(my_request)
63//! .headers(my_headers) // Optional
64//! .handle()
65//! .await?;
66//! ```
67//!
68//! ### Example: Using Client
69//!
70//! The [`Client`] provides a more convenient API that sets owner and provider upfront:
71//! ```rust,ignore
72//! use omnia_sdk::Client;
73//!
74//! // Create a client with owner and provider
75//! let client = Client::new("alice").provider(my_provider);
76//!
77//! // Simple request - await directly (IntoFuture)
78//! let response = client.request(my_request).await?;
79//!
80//! // Request with headers
81//! let response = client
82//! .request(my_request)
83//! .headers(my_headers)
84//! .await?;
85//!
86//! // Or explicitly call handle()
87//! let response = client
88//! .request(my_request)
89//! .headers(my_headers)
90//! .handle()
91//! .await?;
92//! ```
93//!
94//! ## Compile-Time Safety
95//!
96//! The typestate pattern ensures these errors are caught at compile time:
97//! ```rust,compile_fail,ignore
98//!
99//! // ❌ Cannot handle without all required fields
100//! RequestHandler::new().handle().await?; // Won't compile!
101//!
102//! // ❌ Cannot handle without provider
103//! RequestHandler::new()
104//! .owner("alice")
105//! .request(my_request)
106//! .handle().await?; // Won't compile!
107//! ```
108//!
109//! Only when all required fields are set does `.handle()` become available.
110
111use std::error::Error;
112use std::fmt::Debug;
113use std::future::{Future, IntoFuture};
114use std::marker::PhantomData;
115use std::pin::Pin;
116
117use http::HeaderMap;
118
119use crate::api::reply::Reply;
120use crate::api::{Body, Client, Provider};
121
122/// Type alias for a [`RequestHandler`].
123pub type Request<R, P> = RequestHandler<RequestSet<R, P>, NoOwner, NoProvider>;
124
125/// Trait to provide a common interface for request handling.
126pub trait Handler<P: Provider>: Sized {
127 /// The raw input type of the handler.
128 type Input;
129
130 /// The output type of the handler.
131 type Output: Body;
132
133 /// The error type returned by the handler.
134 type Error: Error + Send + Sync;
135
136 /// Parse the input into a `[Handler]` instance.
137 ///
138 /// # Errors
139 ///
140 /// Returns an error if the input cannot be parsed.
141 fn from_input(input: Self::Input) -> Result<Self, Self::Error>;
142
143 /// Initialize a `[RequestHandler]` from raw request.
144 ///
145 /// # Errors
146 ///
147 /// Returns an error if the message cannot be decoded.
148 fn handler(
149 input: Self::Input,
150 ) -> Result<RequestHandler<RequestSet<Self, P>, NoOwner, NoProvider>, Self::Error> {
151 let request = Self::from_input(input)?;
152 let handler = RequestHandler::new().request(request);
153 Ok(handler)
154 }
155
156 /// Implemented by the request handler to process the request.
157 fn handle(
158 self, ctx: Context<P>,
159 ) -> impl Future<Output = Result<Reply<Self::Output>, Self::Error>> + Send;
160}
161
162/// Marker type indicating no owner has been set yet.
163pub struct NoOwner;
164
165/// Marker type indicating the owner has been set.
166pub struct OwnerSet<'a>(&'a str);
167
168/// Marker type indicating no provider has been set yet.
169pub struct NoProvider;
170
171/// Marker type indicating the provider has been set.
172pub struct ProviderSet<'a, P: Provider>(&'a P);
173
174/// Marker type indicating no request has been set yet.
175pub struct NoRequest;
176
177/// Marker type wrapping a request that has been set.
178pub struct RequestSet<R: Handler<P>, P: Provider>(R, PhantomData<P>);
179
180/// Request router.
181///
182/// The router is used to route a request to the appropriate handler with the
183/// owner and headers set.
184/// ```
185#[derive(Debug)]
186pub struct RequestHandler<R, O, P> {
187 request: R,
188 headers: HeaderMap<String>,
189 owner: O,
190 provider: P,
191}
192
193impl Default for RequestHandler<NoRequest, NoOwner, NoProvider> {
194 fn default() -> Self {
195 Self {
196 request: NoRequest,
197 headers: HeaderMap::default(),
198 owner: NoOwner,
199 provider: NoProvider,
200 }
201 }
202}
203
204// ----------------------------------------------
205// New builder
206// ----------------------------------------------
207impl RequestHandler<NoRequest, NoOwner, NoProvider> {
208 /// Create a new (default) `RequestHandler`.
209 #[must_use]
210 pub fn new() -> Self {
211 Self::default()
212 }
213
214 // Internal constructor for creating a `RequestHandler` from a `Client`.
215 pub(crate) fn from_client<R, P>(
216 client: &Client<P>, request: R,
217 ) -> RequestHandler<RequestSet<R, P>, OwnerSet<'_>, ProviderSet<'_, P>>
218 where
219 R: Handler<P>,
220 P: Provider,
221 {
222 RequestHandler {
223 request: RequestSet(request, PhantomData),
224 headers: HeaderMap::default(),
225 owner: OwnerSet(&client.owner),
226 provider: ProviderSet(&client.provider),
227 }
228 }
229}
230
231// ----------------------------------------------
232// Set Owner
233// ----------------------------------------------
234impl<R, P> RequestHandler<R, NoOwner, P> {
235 /// Set the owner (transitions typestate).
236 #[must_use]
237 pub fn owner(self, owner: &str) -> RequestHandler<R, OwnerSet<'_>, P> {
238 RequestHandler {
239 request: self.request,
240 headers: self.headers,
241 owner: OwnerSet(owner),
242 provider: self.provider,
243 }
244 }
245}
246
247// ----------------------------------------------
248// Set Provider
249// ----------------------------------------------
250impl<R, O> RequestHandler<R, O, NoProvider> {
251 /// Set the provider (transitions typestate).
252 pub fn provider<P: Provider>(self, provider: &P) -> RequestHandler<R, O, ProviderSet<'_, P>> {
253 RequestHandler {
254 request: self.request,
255 headers: self.headers,
256 owner: self.owner,
257 provider: ProviderSet(provider),
258 }
259 }
260}
261
262// ----------------------------------------------
263// Set Request
264// ----------------------------------------------
265impl<O, P> RequestHandler<NoRequest, O, P> {
266 /// Set the request (transitions typestate).
267 pub fn request<R, Pr>(self, request: R) -> RequestHandler<RequestSet<R, Pr>, O, P>
268 where
269 R: Handler<Pr>,
270 Pr: Provider,
271 {
272 RequestHandler {
273 request: RequestSet(request, PhantomData),
274 headers: self.headers,
275 owner: self.owner,
276 provider: self.provider,
277 }
278 }
279}
280
281// ----------------------------------------------
282// Headers
283// ----------------------------------------------
284impl<R, O, P> RequestHandler<R, O, P> {
285 /// Set request headers.
286 #[must_use]
287 pub fn headers(mut self, headers: HeaderMap<String>) -> Self {
288 self.headers = headers;
289 self
290 }
291}
292
293// ----------------------------------------------
294// Handle the request
295// ----------------------------------------------
296impl<R, P> RequestHandler<RequestSet<R, P>, OwnerSet<'_>, ProviderSet<'_, P>>
297where
298 R: Handler<P>,
299 P: Provider,
300{
301 /// Handle the request by routing it to the appropriate handler.
302 ///
303 /// # Constraints
304 ///
305 /// This method requires that `R` implements [`Handler<P>`].
306 /// If you see an error about missing trait implementations, ensure your request type
307 /// has the appropriate handler implementation.
308 ///
309 /// # Errors
310 ///
311 /// Returns the error from the underlying handler on failure.
312 #[inline]
313 pub async fn handle(self) -> Result<Reply<R::Output>, <R as Handler<P>>::Error> {
314 let ctx = Context {
315 owner: self.owner.0,
316 provider: self.provider.0,
317 headers: &self.headers,
318 };
319 self.request.0.handle(ctx).await
320 }
321}
322
323// Implement [`IntoFuture`] so that the request can be awaited directly (without
324// needing to call the `handle` method).
325impl<'a, R, P> IntoFuture for RequestHandler<RequestSet<R, P>, OwnerSet<'a>, ProviderSet<'a, P>>
326where
327 P: Provider + 'a,
328 R: Handler<P> + Send + 'a,
329{
330 type IntoFuture = Pin<Box<dyn Future<Output = Self::Output> + Send + 'a>>;
331 type Output = Result<Reply<R::Output>, <R as Handler<P>>::Error>;
332
333 fn into_future(self) -> Self::IntoFuture
334 where
335 R::Output: Body,
336 <R as Handler<P>>::Error: Send,
337 {
338 Box::pin(self.handle())
339 }
340}
341
342/// Request-scoped context passed to [`Handler::handle`].
343///
344/// Bundles common request inputs (owner, provider, headers) into a single
345/// parameter, making handler signatures more ergonomic and easier to extend.
346#[derive(Clone, Copy, Debug)]
347pub struct Context<'a, P: Provider> {
348 /// The owning tenant / namespace for the request.
349 pub owner: &'a str,
350
351 /// The provider implementation used to fulfill the request.
352 pub provider: &'a P,
353
354 /// Request headers (typed).
355 pub headers: &'a HeaderMap<String>,
356}