Skip to main content

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}