Skip to main content

alux_http_typescript/
output.rs

1//! States what a call answers with, in the vocabulary a caller writes.
2
3use crate::TsHttpClient;
4use alux_http::{
5    BytesOutAlg, EmptyOutAlg, FileOutAlg, HeaderOutAlg, HeadersOutAlg, HtmlOutAlg, JsonOutAlg, OutputAlg,
6    RedirectOutAlg, ResultOutAlg, StatusOutAlg, StreamOutAlg, TextOutAlg,
7};
8use alux_shape::{ShapeAlg, ShapeOf};
9use alux_shape_typescript::{TsShape, TsType};
10use core::marker::PhantomData;
11
12/// States what one output kind answers a caller with.
13///
14/// A failure is a rejected call rather than a value, so what a call answers with is what it answers
15/// with when it answers at all.
16pub trait TsOutputAlg<From> {
17    /// Reads the type a caller receives.
18    fn answer(shapes: &TsShape) -> TsType;
19}
20
21/// Answers with whatever the handler's result states.
22pub struct TsJsonOutput;
23
24impl<From> OutputAlg<From> for TsJsonOutput {
25    type Output = From;
26
27    fn output(from: From) -> From {
28        from
29    }
30}
31
32impl<From> TsOutputAlg<From> for TsJsonOutput
33where
34    From: ShapeOf<TsShape, Shape = TsType>,
35{
36    fn answer(shapes: &TsShape) -> TsType {
37        From::shape_of(shapes)
38    }
39}
40
41macro_rules! ts_outputs {
42    ($($output:ident => $answer:ident, $meaning:literal),+ $(,)?) => {
43        $(
44            #[doc = concat!("Answers a caller with what ", $meaning, " states.")]
45            pub struct $output;
46
47            impl<From> OutputAlg<From> for $output {
48                type Output = From;
49
50                fn output(from: From) -> From {
51                    from
52                }
53            }
54
55            impl<From> TsOutputAlg<From> for $output {
56                fn answer(shapes: &TsShape) -> TsType {
57                    shapes.$answer()
58                }
59            }
60        )+
61    };
62}
63
64ts_outputs! {
65    TsTextOutput     => text, "plain text",
66    TsHtmlOutput     => text, "HTML",
67    TsRedirectOutput => unit, "a redirect",
68    TsEmptyOutput    => unit, "an answer with no body",
69}
70
71macro_rules! ts_byte_outputs {
72    ($($output:ident => $meaning:literal),+ $(,)?) => {
73        $(
74            #[doc = concat!("Answers a caller with the bytes ", $meaning, " states.")]
75            pub struct $output;
76
77            impl<From> OutputAlg<From> for $output {
78                type Output = From;
79
80                fn output(from: From) -> From {
81                    from
82                }
83            }
84
85            impl<From> TsOutputAlg<From> for $output {
86                fn answer(shapes: &TsShape) -> TsType {
87                    shapes.bytes(None)
88                }
89            }
90        )+
91    };
92}
93
94ts_byte_outputs! {
95    TsBytesOutput  => "a raw-byte answer",
96    TsFileOutput   => "a streamed file",
97    TsStreamOutput => "a streamed answer",
98}
99
100/// Answers with what the kind beneath states, since a header reaches a caller beside it.
101pub struct TsHeaderOutput<Inner, Name>(PhantomData<fn(Inner, Name)>);
102
103impl<Inner, Name, From> OutputAlg<From> for TsHeaderOutput<Inner, Name> {
104    type Output = From;
105
106    fn output(from: From) -> From {
107        from
108    }
109}
110
111impl<Inner, Name, Value, Rest> TsOutputAlg<(Value, Rest)> for TsHeaderOutput<Inner, Name>
112where
113    Inner: TsOutputAlg<Rest>,
114{
115    fn answer(shapes: &TsShape) -> TsType {
116        Inner::answer(shapes)
117    }
118}
119
120impl HeaderOutAlg for TsHttpClient {
121    type Header<Inner, Name> = TsHeaderOutput<Inner, Name>;
122}
123
124/// Answers with what the kind beneath states, since the headers a product states reach a caller
125/// beside it.
126pub struct TsHeadersOutput<Inner, Headers>(PhantomData<fn(Inner, Headers)>);
127
128impl<Inner, Headers, From> OutputAlg<From> for TsHeadersOutput<Inner, Headers> {
129    type Output = From;
130
131    fn output(from: From) -> From {
132        from
133    }
134}
135
136impl<Inner, Headers, Rest> TsOutputAlg<(Headers, Rest)> for TsHeadersOutput<Inner, Headers>
137where
138    Inner: TsOutputAlg<Rest>,
139{
140    fn answer(shapes: &TsShape) -> TsType {
141        Inner::answer(shapes)
142    }
143}
144
145impl HeadersOutAlg for TsHttpClient {
146    type Headers<Inner, Headers> = TsHeadersOutput<Inner, Headers>;
147}
148
149/// Answers with what the kind beneath states, whatever status the endpoint declared.
150pub struct TsStatusOutput<Inner, const CODE: u16>(PhantomData<Inner>);
151
152impl<Inner, From, const CODE: u16> OutputAlg<From> for TsStatusOutput<Inner, CODE> {
153    type Output = From;
154
155    fn output(from: From) -> From {
156        from
157    }
158}
159
160impl<Inner, From, const CODE: u16> TsOutputAlg<From> for TsStatusOutput<Inner, CODE>
161where
162    Inner: TsOutputAlg<From>,
163{
164    fn answer(shapes: &TsShape) -> TsType {
165        Inner::answer(shapes)
166    }
167}
168
169/// Answers with what the kind beneath states, since a failure rejects the call instead.
170pub struct TsResultOutput<Inner, Error>(PhantomData<fn(Inner, Error)>);
171
172impl<Inner, Error, From> OutputAlg<From> for TsResultOutput<Inner, Error> {
173    type Output = From;
174
175    fn output(from: From) -> From {
176        from
177    }
178}
179
180impl<Inner, Error, Value> TsOutputAlg<Result<Value, Error>> for TsResultOutput<Inner, Error>
181where
182    Inner: TsOutputAlg<Value>,
183{
184    fn answer(shapes: &TsShape) -> TsType {
185        Inner::answer(shapes)
186    }
187}
188
189macro_rules! ts_kinds {
190    ($($alg:ident => $selected:ident, $output:ty),+ $(,)?) => {
191        $(
192            impl $alg for TsHttpClient {
193                type $selected<From> = $output;
194            }
195        )+
196    };
197}
198
199ts_kinds! {
200    JsonOutAlg     => Json, TsJsonOutput,
201    FileOutAlg     => File, TsFileOutput,
202    TextOutAlg     => Text, TsTextOutput,
203    HtmlOutAlg     => Html, TsHtmlOutput,
204    BytesOutAlg    => Bytes, TsBytesOutput,
205    EmptyOutAlg    => Empty, TsEmptyOutput,
206    RedirectOutAlg => Redirect, TsRedirectOutput,
207    StreamOutAlg   => Stream, TsStreamOutput,
208}
209
210impl StatusOutAlg for TsHttpClient {
211    type Status<Inner, const CODE: u16> = TsStatusOutput<Inner, CODE>;
212}
213
214impl ResultOutAlg for TsHttpClient {
215    type Result<Inner, Error> = TsResultOutput<Inner, Error>;
216}