http_endpoint/
endpoint.rs1use std::error::Error;
5
6use http::Error as HttpError;
7use http::HeaderMap;
8use http::Method;
9use http::StatusCode;
10
11use crate::Bytes;
12use crate::Str;
13
14
15pub trait Endpoint {
23 type Input;
26 type Output;
29 type Error: Error + From<HttpError> + From<Self::ConversionError> + 'static;
31 type ConversionError: Error;
33 type ApiError: Error;
35
36 fn base_url() -> Option<Str> {
41 None
42 }
43
44 fn method() -> Method {
48 Method::GET
49 }
50
51 fn path(input: &Self::Input) -> Str;
53
54 #[allow(unused)]
58 fn query(input: &Self::Input) -> Result<Option<Str>, Self::ConversionError> {
59 Ok(None)
60 }
61
62 #[allow(unused)]
64 fn headers(input: &Self::Input) -> Result<Option<HeaderMap>, Self::ConversionError> {
65 Ok(None)
66 }
67
68 #[allow(unused)]
72 fn body(input: &Self::Input) -> Result<Option<Bytes>, Self::ConversionError> {
73 Ok(None)
74 }
75
76 fn parse(body: &[u8]) -> Result<Self::Output, Self::ConversionError>;
78
79 fn parse_err(body: &[u8]) -> Result<Self::ApiError, Vec<u8>>;
81
82 #[doc(hidden)]
88 fn evaluate(status: StatusCode, body: &[u8]) -> Result<Self::Output, Self::Error>;
89}
90
91
92#[macro_export]
95macro_rules! EndpointDef {
96 ( $(#[$docs:meta])* $pub:vis $name:ident($in:ty),
97 Ok => $out:ty, [$($(#[$ok_docs:meta])* $ok_status:ident,)*],
100 Err => $err:ident, [$($(#[$err_docs:meta])* $err_status:ident => $variant:ident,)*],
101 ConversionErr => $conv_err:ty,
102 ApiErr => $api_err:ty,
103 $($defs:tt)* ) => {
104
105 $(#[$docs])*
106 #[derive(Clone, Copy, Debug)]
107 $pub struct $name;
108
109 #[allow(unused_qualifications)]
112 #[derive(Debug)]
113 $pub enum $err {
114 $(
115 $(#[$err_docs])*
116 $variant(Result<$api_err, Vec<u8>>),
117 )*
118 UnexpectedStatus(::http::StatusCode, Result<$api_err, Vec<u8>>),
121 Http(::http::Error),
123 Conversion($conv_err),
125 }
126
127 #[allow(unused_qualifications)]
128 impl ::std::fmt::Display for $err {
129 fn fmt(&self, fmt: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
130 fn format_message(message: &Result<$api_err, Vec<u8>>) -> String {
131 match message {
132 Ok(err) => err.to_string(),
133 Err(body) => {
134 match ::std::str::from_utf8(&body) {
135 Ok(body) => format!("{}", body),
136 Err(err) => format!("{:?}", body),
137 }
138 },
139 }
140 }
141
142 match self {
143 $(
144 $err::$variant(message) => {
145 let status = ::http::StatusCode::$err_status;
146 let message = format_message(message);
147 write!(fmt, "HTTP status {}: {}", status, message)
148 },
149 )*
150 $err::UnexpectedStatus(status, message) => {
151 let message = format_message(message);
152 write!(fmt, "Unexpected HTTP status {}: {}", status, message)
153 },
154 $err::Http(err) => write!(fmt, "{}", err),
155 $err::Conversion(err) => write!(fmt, "{}", err),
156 }
157 }
158 }
159
160 #[allow(unused_qualifications)]
161 impl ::std::error::Error for $err {
162 fn source(&self) -> Option<&(dyn ::std::error::Error + 'static)> {
163 match self {
164 $(
165 $err::$variant(..) => None,
166 )*
167 $err::UnexpectedStatus(..) => None,
168 $err::Http(err) => err.source(),
169 $err::Conversion(err) => err.source(),
170 }
171 }
172 }
173
174 #[allow(unused_qualifications)]
175 impl ::std::convert::From<::http::Error> for $err {
176 fn from(src: ::http::Error) -> Self {
177 $err::Http(src)
178 }
179 }
180
181 #[allow(unused_qualifications)]
182 impl ::std::convert::From<$conv_err> for $err {
183 fn from(src: $conv_err) -> Self {
184 $err::Conversion(src)
185 }
186 }
187
188 #[allow(unused_qualifications)]
189 impl ::std::convert::From<$err> for ::http_endpoint::Error<$conv_err> {
190 fn from(src: $err) -> Self {
191 match src {
192 $(
193 $err::$variant(result) => {
194 let status = ::http::StatusCode::$err_status;
195 match result {
196 Ok(err) => {
197 ::http_endpoint::Error::HttpStatus(status, err.to_string().into_bytes())
198 },
199 Err(data) => ::http_endpoint::Error::HttpStatus(status, data),
200 }
201 },
202 )*
203 $err::UnexpectedStatus(status, result) => {
204 match result {
205 Ok(err) => {
206 ::http_endpoint::Error::HttpStatus(status, err.to_string().into_bytes())
207 },
208 Err(data) => ::http_endpoint::Error::HttpStatus(status, data),
209 }
210 },
211 $err::Http(err) => ::http_endpoint::Error::Http(err),
212 $err::Conversion(err) => ::http_endpoint::Error::Conversion(err),
213 }
214 }
215 }
216
217 #[allow(unused_qualifications)]
218 impl ::http_endpoint::Endpoint for $name {
219 type Input = $in;
220 type Output = $out;
221 type Error = $err;
222 type ConversionError = $conv_err;
223 type ApiError = $api_err;
224
225 $($defs)*
226
227 #[allow(unused_qualifications)]
228 fn evaluate(
229 status: ::http::StatusCode,
230 body: &[u8],
231 ) -> Result<$out, $err> {
232 match status {
233 $(
234 ::http::StatusCode::$ok_status => {
235 <$name as ::http_endpoint::Endpoint>::parse(&body).map_err($err::from)
236 },
237 )*
238 status => {
239 let res = <$name as ::http_endpoint::Endpoint>::parse_err(&body);
240 match status {
241 $(
242 ::http::StatusCode::$err_status => {
243 Err($err::$variant(res))
244 },
245 )*
246 _ => Err($err::UnexpectedStatus(status, res)),
247 }
248 },
249 }
250 }
251 }
252 };
253}