Skip to main content

alux_http/
output.rs

1//! States what an endpoint answers with, before any framework can convert it.
2
3use crate::WithAlg;
4use core::marker::PhantomData;
5
6/// Transforms an inferred handler result into its portable API output.
7///
8/// Converter families are selected from an endpoint's output kind. The handler
9/// result supplies `From`, so API declarations never repeat it.
10pub trait OutputAlg<From> {
11    /// The transport value produced from the semantic handler result.
12    type Output;
13
14    /// Converts a handler result into the declared API output.
15    fn output(from: From) -> Self::Output;
16}
17
18/// Resolves a portable output kind through an interpreter.
19pub trait OutputKindAlg<Interpreter: ?Sized, From> {
20    /// The concrete converter chosen by `Interpreter` for this output kind.
21    ///
22    /// That the converter reads `From` is required where the endpoint is compiled rather than
23    /// here, so a kind may answer for some results and not others. An endpoint stating no body
24    /// converts a handler that returns nothing, and an endpoint stating a failure converts a
25    /// handler that can fail.
26    type Transform;
27}
28
29macro_rules! output_kinds {
30    ($($declaration:ident => $kind:ident, $alg:ident, $selected:ident, $meaning:literal),+ $(,)?) => {
31        $(
32            #[doc = concat!("Selects the converter used for ", $meaning, " API outputs.")]
33            pub trait $alg {
34                #[doc = concat!("The ", $meaning, " converter selected for `From`.")]
35                type $selected<From>;
36            }
37
38            #[doc = concat!("Selects ", $meaning, " output semantics.")]
39            #[derive(Debug, Default)]
40            pub struct $kind;
41
42            impl<Interpreter, From> OutputKindAlg<Interpreter, From> for $kind
43            where
44                Interpreter: $alg + ?Sized,
45            {
46                type Transform = Interpreter::$selected<From>;
47            }
48        )+
49    };
50}
51
52with_output_kinds!(output_kinds);
53
54/// Selects the converter used to answer with a declared status.
55pub trait StatusOutAlg {
56    /// The converter answering with `CODE` and the body `Inner` states.
57    type Status<Inner, const CODE: u16>;
58}
59
60/// Answers with `CODE` and the body `Kind` states.
61///
62/// The status is part of what an endpoint is declared to answer, so it is stated once in the
63/// program rather than chosen inside a handler.
64#[derive(Debug, Default)]
65pub struct StatusOut<Kind, const CODE: u16>(PhantomData<Kind>);
66
67impl<Interpreter, From, Kind, const CODE: u16> OutputKindAlg<Interpreter, From> for StatusOut<Kind, CODE>
68where
69    Interpreter: StatusOutAlg + ?Sized,
70    Kind: OutputKindAlg<Interpreter, From>,
71{
72    type Transform = Interpreter::Status<Kind::Transform, CODE>;
73}
74
75/// Selects the converter used to answer with a header beside a body.
76pub trait HeaderOutAlg {
77    /// The converter writing `Name` beside the body `Inner` states.
78    type Header<Inner, Name>;
79}
80
81/// Answers with a header the handler states, beside the body `Kind` states.
82///
83/// The handler answers with the header's value and the body, in that order, because a header whose
84/// value an endpoint cannot know is a header only the handler can state. Which header it is, is
85/// stated here: a name is all a header is to a program.
86#[derive(Debug, Default)]
87pub struct HeaderOut<Kind, Name>(PhantomData<fn(Kind, Name)>);
88
89impl<Interpreter, Value, Rest, Kind, Name> OutputKindAlg<Interpreter, (Value, Rest)> for HeaderOut<Kind, Name>
90where
91    Interpreter: HeaderOutAlg + ?Sized,
92    Kind: OutputKindAlg<Interpreter, Rest>,
93{
94    type Transform = Interpreter::Header<Kind::Transform, Name>;
95}
96
97/// Selects the converter used to answer with the headers a named product states, beside a body.
98pub trait HeadersOutAlg {
99    /// The converter writing each value `Headers` states beside the body `Inner` states.
100    type Headers<Inner, Headers>;
101}
102
103/// Answers with the headers a named product states, beside the body `Kind` states.
104///
105/// The output twin of reading headers into a product: each member is a header, named by the words
106/// its member name states, so `cache_control` is `cache-control`. A member stating nothing is not
107/// written, and a member stating many values writes one header for each, as `set-cookie` needs. The
108/// handler answers with the product and the body, in that order.
109#[derive(Debug, Default)]
110pub struct HeadersOut<Kind, Headers>(PhantomData<fn(Kind, Headers)>);
111
112impl<Interpreter, Headers, Rest, Kind> OutputKindAlg<Interpreter, (Headers, Rest)> for HeadersOut<Kind, Headers>
113where
114    Interpreter: HeadersOutAlg + ?Sized,
115    Kind: OutputKindAlg<Interpreter, Rest>,
116{
117    type Transform = Interpreter::Headers<Kind::Transform, Headers>;
118}
119
120/// Selects the converter used for a handler that can fail.
121pub trait ResultOutAlg {
122    /// The converter answering with `Inner` on success and with what `Error` means otherwise.
123    type Result<Inner, Error>;
124}
125
126/// Answers with `Kind` when the handler succeeded, and with what its failure means otherwise.
127///
128/// A handler that can fail returns a `Result`, so this kind reads one. What a failure means is
129/// stated by [`HttpErrorAlg`](crate::HttpErrorAlg) on the error itself.
130#[derive(Debug, Default)]
131pub struct ResultOut<Kind>(PhantomData<Kind>);
132
133impl<Interpreter, Kind, Value, Error> OutputKindAlg<Interpreter, Result<Value, Error>> for ResultOut<Kind>
134where
135    Interpreter: ResultOutAlg + ?Sized,
136    Kind: OutputKindAlg<Interpreter, Value>,
137{
138    type Transform = Interpreter::Result<Kind::Transform, Error>;
139}
140
141/// Holds the wrappers a declaration states before its kind, the outermost first.
142///
143/// A declaration reads from the outside in: `.out_header::<ETag>().result().json()` answers with an
144/// `ETag` around a result around a JSON body, and its handler returns `(etag, Result<body, E>)`. The
145/// kind closes the declaration, folding the wrappers held here around it.
146///
147/// Nothing wraps a declaration its kind has closed:
148///
149/// ```compile_fail,E0277
150/// use alux_http::HttpProgramBuilder;
151///
152/// let syntax = HttpProgramBuilder;
153/// let _ = syntax.op(()).json().result();
154/// ```
155#[derive(Debug, Default)]
156pub struct Pending<Wrappers>(PhantomData<Wrappers>);
157
158/// States one wrapper as the kind it makes of the kind inside it.
159pub trait WrapAlg {
160    /// The kind this wrapper makes around `Inner`.
161    type Wrap<Inner>;
162}
163
164impl<const CODE: u16> WrapAlg for StatusOut<(), CODE> {
165    type Wrap<Inner> = StatusOut<Inner, CODE>;
166}
167
168impl<Name> WrapAlg for HeaderOut<(), Name> {
169    type Wrap<Inner> = HeaderOut<Inner, Name>;
170}
171
172impl<Headers> WrapAlg for HeadersOut<(), Headers> {
173    type Wrap<Inner> = HeadersOut<Inner, Headers>;
174}
175
176impl WrapAlg for ResultOut<()> {
177    type Wrap<Inner> = ResultOut<Inner>;
178}
179
180/// Reads a declaration still open to wrappers: one stating nothing yet, or wrappers and no kind.
181#[diagnostic::on_unimplemented(
182    message = "`{Self}` already states its output kind",
183    note = "wrappers such as `.status()`, `.out_header()`, `.out_headers()`, and `.result()` come before the kind, which closes the declaration"
184)]
185pub trait OpenAlg {
186    /// The declaration with `Wrapper` inside every wrapper already stated.
187    type With<Wrapper>;
188}
189
190impl OpenAlg for () {
191    type With<Wrapper> = Pending<(Wrapper,)>;
192}
193
194impl<Wrappers> OpenAlg for Pending<Wrappers>
195where
196    Wrappers: WithAlg,
197{
198    type With<Wrapper> = Pending<Wrappers::With<Wrapper>>;
199}
200
201/// Closes a declaration with its kind, folding the wrappers it states around that kind.
202#[diagnostic::on_unimplemented(
203    message = "`{Self}` already states its output kind",
204    note = "a declaration states one kind, last, after the wrappers around it"
205)]
206pub trait CloseAlg<Kind> {
207    /// The kind the whole declaration answers with.
208    type Closed;
209}
210
211impl<Kind> CloseAlg<Kind> for () {
212    type Closed = Kind;
213}
214
215impl<Kind, Wrappers> CloseAlg<Kind> for Pending<Wrappers>
216where
217    Wrappers: FoldAlg<Kind>,
218{
219    type Closed = Wrappers::Folded;
220}
221
222/// Folds wrappers around a kind, the first of them outermost.
223pub trait FoldAlg<Kind> {
224    /// The kind the wrappers make around `Kind`.
225    type Folded;
226}
227
228macro_rules! fold_wrappers {
229    () => {
230        impl<Kind> FoldAlg<Kind> for () {
231            type Folded = Kind;
232        }
233    };
234    ($first:ident $(, $rest:ident)*) => {
235        impl<Kind, $first $(, $rest)*> FoldAlg<Kind> for ($first, $($rest,)*)
236        where
237            $first: WrapAlg,
238            ($($rest,)*): FoldAlg<Kind>,
239        {
240            type Folded = $first::Wrap<<($($rest,)*) as FoldAlg<Kind>>::Folded>;
241        }
242
243        fold_wrappers!($($rest),*);
244    };
245}
246
247fold_wrappers!(W1, W2, W3, W4, W5, W6, W7, W8, W9, W10, W11, W12, W13, W14, W15, W16);