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, HtmlOutAlg, JsonOutAlg, OutputAlg, RedirectOutAlg,
6    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, whatever status the endpoint declared.
125pub struct TsStatusOutput<Inner, const CODE: u16>(PhantomData<Inner>);
126
127impl<Inner, From, const CODE: u16> OutputAlg<From> for TsStatusOutput<Inner, CODE> {
128    type Output = From;
129
130    fn output(from: From) -> From {
131        from
132    }
133}
134
135impl<Inner, From, const CODE: u16> TsOutputAlg<From> for TsStatusOutput<Inner, CODE>
136where
137    Inner: TsOutputAlg<From>,
138{
139    fn answer(shapes: &TsShape) -> TsType {
140        Inner::answer(shapes)
141    }
142}
143
144/// Answers with what the kind beneath states, since a failure rejects the call instead.
145pub struct TsResultOutput<Inner, Error>(PhantomData<fn(Inner, Error)>);
146
147impl<Inner, Error, From> OutputAlg<From> for TsResultOutput<Inner, Error> {
148    type Output = From;
149
150    fn output(from: From) -> From {
151        from
152    }
153}
154
155impl<Inner, Error, Value> TsOutputAlg<Result<Value, Error>> for TsResultOutput<Inner, Error>
156where
157    Inner: TsOutputAlg<Value>,
158{
159    fn answer(shapes: &TsShape) -> TsType {
160        Inner::answer(shapes)
161    }
162}
163
164macro_rules! ts_kinds {
165    ($($alg:ident => $selected:ident, $output:ty),+ $(,)?) => {
166        $(
167            impl $alg for TsHttpClient {
168                type $selected<From> = $output;
169            }
170        )+
171    };
172}
173
174ts_kinds! {
175    JsonOutAlg     => Json, TsJsonOutput,
176    FileOutAlg     => File, TsFileOutput,
177    TextOutAlg     => Text, TsTextOutput,
178    HtmlOutAlg     => Html, TsHtmlOutput,
179    BytesOutAlg    => Bytes, TsBytesOutput,
180    EmptyOutAlg    => Empty, TsEmptyOutput,
181    RedirectOutAlg => Redirect, TsRedirectOutput,
182    StreamOutAlg   => Stream, TsStreamOutput,
183}
184
185impl StatusOutAlg for TsHttpClient {
186    type Status<Inner, const CODE: u16> = TsStatusOutput<Inner, CODE>;
187}
188
189impl ResultOutAlg for TsHttpClient {
190    type Result<Inner, Error> = TsResultOutput<Inner, Error>;
191}