r1_api_layer/client/
mod.rs

1use async_trait::async_trait;
2use futures::{Stream, future, future::BoxFuture, stream, future::TryFutureExt, future::FutureExt, stream::StreamExt};
3use hyper::header::{HeaderName, HeaderValue, CONTENT_TYPE};
4use hyper::{Body, Request, Response, service::Service, Uri};
5use percent_encoding::{utf8_percent_encode, AsciiSet};
6use std::borrow::Cow;
7use std::convert::TryInto;
8use std::io::{ErrorKind, Read};
9use std::error::Error;
10use std::future::Future;
11use std::fmt;
12use std::marker::PhantomData;
13use std::path::Path;
14use std::sync::{Arc, Mutex};
15use std::str;
16use std::str::FromStr;
17use std::string::ToString;
18use std::task::{Context, Poll};
19use swagger::{ApiError, AuthData, BodyExt, Connector, DropContextService, Has, XSpanIdString};
20use url::form_urlencoded;
21
22
23use crate::models;
24use crate::header;
25
26/// https://url.spec.whatwg.org/#fragment-percent-encode-set
27#[allow(dead_code)]
28const FRAGMENT_ENCODE_SET: &AsciiSet = &percent_encoding::CONTROLS
29    .add(b' ').add(b'"').add(b'<').add(b'>').add(b'`');
30
31/// This encode set is used for object IDs
32///
33/// Aside from the special characters defined in the `PATH_SEGMENT_ENCODE_SET`,
34/// the vertical bar (|) is encoded.
35#[allow(dead_code)]
36const ID_ENCODE_SET: &AsciiSet = &FRAGMENT_ENCODE_SET.add(b'|');
37
38use crate::{Api,
39     BobbyDeleteResponse,
40     GamePostResponse,
41     MapsGetResponse,
42     RegisterPostResponse,
43     StatisticsGetResponse,
44     MapLevelGetResponse,
45     StatisticsUuidGetResponse,
46     UserUuidGetResponse
47     };
48
49/// Convert input into a base path, e.g. "http://example:123". Also checks the scheme as it goes.
50fn into_base_path(input: impl TryInto<Uri, Error=hyper::http::uri::InvalidUri>, correct_scheme: Option<&'static str>) -> Result<String, ClientInitError> {
51    // First convert to Uri, since a base path is a subset of Uri.
52    let uri = input.try_into()?;
53
54    let scheme = uri.scheme_str().ok_or(ClientInitError::InvalidScheme)?;
55
56    // Check the scheme if necessary
57    if let Some(correct_scheme) = correct_scheme {
58        if scheme != correct_scheme {
59            return Err(ClientInitError::InvalidScheme);
60        }
61    }
62
63    let host = uri.host().ok_or(ClientInitError::MissingHost)?;
64    let port = uri.port_u16().map(|x| format!(":{}", x)).unwrap_or_default();
65    Ok(format!("{}://{}{}{}", scheme, host, port, uri.path().trim_end_matches('/')))
66}
67
68/// A client that implements the API by making HTTP calls out to a server.
69pub struct Client<S, C> where
70    S: Service<
71           (Request<Body>, C),
72           Response=Response<Body>> + Clone + Sync + Send + 'static,
73    S::Future: Send + 'static,
74    S::Error: Into<crate::ServiceError> + fmt::Display,
75    C: Clone + Send + Sync + 'static
76{
77    /// Inner service
78    client_service: S,
79
80    /// Base path of the API
81    base_path: String,
82
83    /// Marker
84    marker: PhantomData<fn(C)>,
85}
86
87impl<S, C> fmt::Debug for Client<S, C> where
88    S: Service<
89           (Request<Body>, C),
90           Response=Response<Body>> + Clone + Sync + Send + 'static,
91    S::Future: Send + 'static,
92    S::Error: Into<crate::ServiceError> + fmt::Display,
93    C: Clone + Send + Sync + 'static
94{
95    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
96        write!(f, "Client {{ base_path: {} }}", self.base_path)
97    }
98}
99
100impl<S, C> Clone for Client<S, C> where
101    S: Service<
102           (Request<Body>, C),
103           Response=Response<Body>> + Clone + Sync + Send + 'static,
104    S::Future: Send + 'static,
105    S::Error: Into<crate::ServiceError> + fmt::Display,
106    C: Clone + Send + Sync + 'static
107{
108    fn clone(&self) -> Self {
109        Self {
110            client_service: self.client_service.clone(),
111            base_path: self.base_path.clone(),
112            marker: PhantomData,
113        }
114    }
115}
116
117impl<Connector, C> Client<DropContextService<hyper::client::Client<Connector, Body>, C>, C> where
118    Connector: hyper::client::connect::Connect + Clone + Send + Sync + 'static,
119    C: Clone + Send + Sync + 'static,
120{
121    /// Create a client with a custom implementation of hyper::client::Connect.
122    ///
123    /// Intended for use with custom implementations of connect for e.g. protocol logging
124    /// or similar functionality which requires wrapping the transport layer. When wrapping a TCP connection,
125    /// this function should be used in conjunction with `swagger::Connector::builder()`.
126    ///
127    /// For ordinary tcp connections, prefer the use of `try_new_http`, `try_new_https`
128    /// and `try_new_https_mutual`, to avoid introducing a dependency on the underlying transport layer.
129    ///
130    /// # Arguments
131    ///
132    /// * `base_path` - base path of the client API, i.e. "http://www.my-api-implementation.com"
133    /// * `protocol` - Which protocol to use when constructing the request url, e.g. `Some("http")`
134    /// * `connector` - Implementation of `hyper::client::Connect` to use for the client
135    pub fn try_new_with_connector(
136        base_path: &str,
137        protocol: Option<&'static str>,
138        connector: Connector,
139    ) -> Result<Self, ClientInitError>
140    {
141        let client_service = hyper::client::Client::builder().build(connector);
142        let client_service = DropContextService::new(client_service);
143
144        Ok(Self {
145            client_service,
146            base_path: into_base_path(base_path, protocol)?,
147            marker: PhantomData,
148        })
149    }
150}
151
152#[derive(Debug, Clone)]
153pub enum HyperClient {
154    Http(hyper::client::Client<hyper::client::HttpConnector, Body>),
155    Https(hyper::client::Client<HttpsConnector, Body>),
156}
157
158impl Service<Request<Body>> for HyperClient {
159    type Response = Response<Body>;
160    type Error = hyper::Error;
161    type Future = hyper::client::ResponseFuture;
162
163    fn poll_ready(&mut self, cx: &mut Context) -> Poll<Result<(), Self::Error>> {
164       match self {
165          HyperClient::Http(client) => client.poll_ready(cx),
166          HyperClient::Https(client) => client.poll_ready(cx),
167       }
168    }
169
170    fn call(&mut self, req: Request<Body>) -> Self::Future {
171       match self {
172          HyperClient::Http(client) => client.call(req),
173          HyperClient::Https(client) => client.call(req)
174       }
175    }
176}
177
178impl<C> Client<DropContextService<HyperClient, C>, C> where
179    C: Clone + Send + Sync + 'static,
180{
181    /// Create an HTTP client.
182    ///
183    /// # Arguments
184    /// * `base_path` - base path of the client API, i.e. "http://www.my-api-implementation.com"
185    pub fn try_new(
186        base_path: &str,
187    ) -> Result<Self, ClientInitError> {
188        let uri = Uri::from_str(base_path)?;
189
190        let scheme = uri.scheme_str().ok_or(ClientInitError::InvalidScheme)?;
191        let scheme = scheme.to_ascii_lowercase();
192
193        let connector = Connector::builder();
194
195        let client_service = match scheme.as_str() {
196            "http" => {
197                HyperClient::Http(hyper::client::Client::builder().build(connector.build()))
198            },
199            "https" => {
200                let connector = connector.https()
201                   .build()
202                   .map_err(ClientInitError::SslError)?;
203                HyperClient::Https(hyper::client::Client::builder().build(connector))
204            },
205            _ => {
206                return Err(ClientInitError::InvalidScheme);
207            }
208        };
209
210        let client_service = DropContextService::new(client_service);
211
212        Ok(Self {
213            client_service,
214            base_path: into_base_path(base_path, None)?,
215            marker: PhantomData,
216        })
217    }
218}
219
220impl<C> Client<DropContextService<hyper::client::Client<hyper::client::HttpConnector, Body>, C>, C> where
221    C: Clone + Send + Sync + 'static
222{
223    /// Create an HTTP client.
224    ///
225    /// # Arguments
226    /// * `base_path` - base path of the client API, i.e. "http://www.my-api-implementation.com"
227    pub fn try_new_http(
228        base_path: &str,
229    ) -> Result<Self, ClientInitError> {
230        let http_connector = Connector::builder().build();
231
232        Self::try_new_with_connector(base_path, Some("http"), http_connector)
233    }
234}
235
236#[cfg(any(target_os = "macos", target_os = "windows", target_os = "ios"))]
237type HttpsConnector = hyper_tls::HttpsConnector<hyper::client::HttpConnector>;
238
239#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
240type HttpsConnector = hyper_openssl::HttpsConnector<hyper::client::HttpConnector>;
241
242impl<C> Client<DropContextService<hyper::client::Client<HttpsConnector, Body>, C>, C> where
243    C: Clone + Send + Sync + 'static
244{
245    /// Create a client with a TLS connection to the server
246    ///
247    /// # Arguments
248    /// * `base_path` - base path of the client API, i.e. "https://www.my-api-implementation.com"
249    pub fn try_new_https(base_path: &str) -> Result<Self, ClientInitError>
250    {
251        let https_connector = Connector::builder()
252            .https()
253            .build()
254            .map_err(ClientInitError::SslError)?;
255        Self::try_new_with_connector(base_path, Some("https"), https_connector)
256    }
257
258    /// Create a client with a TLS connection to the server using a pinned certificate
259    ///
260    /// # Arguments
261    /// * `base_path` - base path of the client API, i.e. "https://www.my-api-implementation.com"
262    /// * `ca_certificate` - Path to CA certificate used to authenticate the server
263    #[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
264    pub fn try_new_https_pinned<CA>(
265        base_path: &str,
266        ca_certificate: CA,
267    ) -> Result<Self, ClientInitError>
268    where
269        CA: AsRef<Path>,
270    {
271        let https_connector = Connector::builder()
272            .https()
273            .pin_server_certificate(ca_certificate)
274            .build()
275            .map_err(ClientInitError::SslError)?;
276        Self::try_new_with_connector(base_path, Some("https"), https_connector)
277    }
278
279    /// Create a client with a mutually authenticated TLS connection to the server.
280    ///
281    /// # Arguments
282    /// * `base_path` - base path of the client API, i.e. "https://www.my-api-implementation.com"
283    /// * `ca_certificate` - Path to CA certificate used to authenticate the server
284    /// * `client_key` - Path to the client private key
285    /// * `client_certificate` - Path to the client's public certificate associated with the private key
286    #[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
287    pub fn try_new_https_mutual<CA, K, D>(
288        base_path: &str,
289        ca_certificate: CA,
290        client_key: K,
291        client_certificate: D,
292    ) -> Result<Self, ClientInitError>
293    where
294        CA: AsRef<Path>,
295        K: AsRef<Path>,
296        D: AsRef<Path>,
297    {
298        let https_connector = Connector::builder()
299            .https()
300            .pin_server_certificate(ca_certificate)
301            .client_authentication(client_key, client_certificate)
302            .build()
303            .map_err(ClientInitError::SslError)?;
304        Self::try_new_with_connector(base_path, Some("https"), https_connector)
305    }
306}
307
308impl<S, C> Client<S, C> where
309    S: Service<
310           (Request<Body>, C),
311           Response=Response<Body>> + Clone + Sync + Send + 'static,
312    S::Future: Send + 'static,
313    S::Error: Into<crate::ServiceError> + fmt::Display,
314    C: Clone + Send + Sync + 'static
315{
316    /// Constructor for creating a `Client` by passing in a pre-made `hyper::service::Service` /
317    /// `tower::Service`
318    ///
319    /// This allows adding custom wrappers around the underlying transport, for example for logging.
320    pub fn try_new_with_client_service(
321        client_service: S,
322        base_path: &str,
323    ) -> Result<Self, ClientInitError>
324    {
325        Ok(Self {
326            client_service,
327            base_path: into_base_path(base_path, None)?,
328            marker: PhantomData,
329        })
330    }
331}
332
333/// Error type failing to create a Client
334#[derive(Debug)]
335pub enum ClientInitError {
336    /// Invalid URL Scheme
337    InvalidScheme,
338
339    /// Invalid URI
340    InvalidUri(hyper::http::uri::InvalidUri),
341
342    /// Missing Hostname
343    MissingHost,
344
345    /// SSL Connection Error
346    #[cfg(any(target_os = "macos", target_os = "windows", target_os = "ios"))]
347    SslError(native_tls::Error),
348
349    /// SSL Connection Error
350    #[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
351    SslError(openssl::error::ErrorStack),
352}
353
354impl From<hyper::http::uri::InvalidUri> for ClientInitError {
355    fn from(err: hyper::http::uri::InvalidUri) -> ClientInitError {
356        ClientInitError::InvalidUri(err)
357    }
358}
359
360impl fmt::Display for ClientInitError {
361    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
362        let s: &dyn fmt::Debug = self;
363        s.fmt(f)
364    }
365}
366
367impl Error for ClientInitError {
368    fn description(&self) -> &str {
369        "Failed to produce a hyper client."
370    }
371}
372
373#[async_trait]
374impl<S, C> Api<C> for Client<S, C> where
375    S: Service<
376       (Request<Body>, C),
377       Response=Response<Body>> + Clone + Sync + Send + 'static,
378    S::Future: Send + 'static,
379    S::Error: Into<crate::ServiceError> + fmt::Display,
380    C: Has<XSpanIdString>  + Clone + Send + Sync + 'static,
381{
382    fn poll_ready(&self, cx: &mut Context) -> Poll<Result<(), crate::ServiceError>> {
383        match self.client_service.clone().poll_ready(cx) {
384            Poll::Ready(Err(e)) => Poll::Ready(Err(e.into())),
385            Poll::Ready(Ok(o)) => Poll::Ready(Ok(o)),
386            Poll::Pending => Poll::Pending,
387        }
388    }
389
390    async fn bobby_delete(
391        &self,
392        context: &C) -> Result<BobbyDeleteResponse, ApiError>
393    {
394        let mut client_service = self.client_service.clone();
395        let mut uri = format!(
396            "{}/bobby",
397            self.base_path
398        );
399
400        // Query parameters
401        let query_string = {
402            let mut query_string = form_urlencoded::Serializer::new("".to_owned());
403            query_string.finish()
404        };
405        if !query_string.is_empty() {
406            uri += "?";
407            uri += &query_string;
408        }
409
410        let uri = match Uri::from_str(&uri) {
411            Ok(uri) => uri,
412            Err(err) => return Err(ApiError(format!("Unable to build URI: {}", err))),
413        };
414
415        let mut request = match Request::builder()
416            .method("DELETE")
417            .uri(uri)
418            .body(Body::empty()) {
419                Ok(req) => req,
420                Err(e) => return Err(ApiError(format!("Unable to create request: {}", e)))
421        };
422
423        let header = HeaderValue::from_str(Has::<XSpanIdString>::get(context).0.as_str());
424        request.headers_mut().insert(HeaderName::from_static("x-span-id"), match header {
425            Ok(h) => h,
426            Err(e) => return Err(ApiError(format!("Unable to create X-Span ID header value: {}", e)))
427        });
428
429        let response = client_service.call((request, context.clone()))
430            .map_err(|e| ApiError(format!("No response received: {}", e))).await?;
431
432        match response.status().as_u16() {
433            200 => {
434                Ok(
435                    BobbyDeleteResponse::DroppedAllData
436                )
437            }
438            409 => {
439                Ok(
440                    BobbyDeleteResponse::WrongPassphraseProvided
441                )
442            }
443            code => {
444                let headers = response.headers().clone();
445                let body = response.into_body()
446                       .take(100)
447                       .into_raw().await;
448                Err(ApiError(format!("Unexpected response code {}:\n{:?}\n\n{}",
449                    code,
450                    headers,
451                    match body {
452                        Ok(body) => match String::from_utf8(body) {
453                            Ok(body) => body,
454                            Err(e) => format!("<Body was not UTF8: {:?}>", e),
455                        },
456                        Err(e) => format!("<Failed to read body: {}>", e),
457                    }
458                )))
459            }
460        }
461    }
462
463    async fn game_post(
464        &self,
465        param_game_end_dto: models::GameEndDto,
466        context: &C) -> Result<GamePostResponse, ApiError>
467    {
468        let mut client_service = self.client_service.clone();
469        let mut uri = format!(
470            "{}/game",
471            self.base_path
472        );
473
474        // Query parameters
475        let query_string = {
476            let mut query_string = form_urlencoded::Serializer::new("".to_owned());
477            query_string.finish()
478        };
479        if !query_string.is_empty() {
480            uri += "?";
481            uri += &query_string;
482        }
483
484        let uri = match Uri::from_str(&uri) {
485            Ok(uri) => uri,
486            Err(err) => return Err(ApiError(format!("Unable to build URI: {}", err))),
487        };
488
489        let mut request = match Request::builder()
490            .method("POST")
491            .uri(uri)
492            .body(Body::empty()) {
493                Ok(req) => req,
494                Err(e) => return Err(ApiError(format!("Unable to create request: {}", e)))
495        };
496
497        // Consumes basic body
498        // Body parameter
499        let body = serde_json::to_string(&param_game_end_dto).expect("impossible to fail to serialize");
500        *request.body_mut() = Body::from(body);
501
502        let header = "application/json";
503        request.headers_mut().insert(CONTENT_TYPE, match HeaderValue::from_str(header) {
504            Ok(h) => h,
505            Err(e) => return Err(ApiError(format!("Unable to create header: {} - {}", header, e)))
506        });
507
508        let header = HeaderValue::from_str(Has::<XSpanIdString>::get(context).0.as_str());
509        request.headers_mut().insert(HeaderName::from_static("x-span-id"), match header {
510            Ok(h) => h,
511            Err(e) => return Err(ApiError(format!("Unable to create X-Span ID header value: {}", e)))
512        });
513
514        let response = client_service.call((request, context.clone()))
515            .map_err(|e| ApiError(format!("No response received: {}", e))).await?;
516
517        match response.status().as_u16() {
518            200 => {
519                let body = response.into_body();
520                let body = body
521                        .into_raw()
522                        .map_err(|e| ApiError(format!("Failed to read response: {}", e))).await?;
523
524                let body = str::from_utf8(&body)
525                    .map_err(|e| ApiError(format!("Response was not valid UTF8: {}", e)))?;
526                let body = serde_json::from_str::<models::ResultDto>(body)
527                    .map_err(|e| ApiError(format!("Response body did not match the schema: {}", e)))?;
528
529
530                Ok(GamePostResponse::GameResultDetails
531                    (body)
532                )
533            }
534            code => {
535                let headers = response.headers().clone();
536                let body = response.into_body()
537                       .take(100)
538                       .into_raw().await;
539                Err(ApiError(format!("Unexpected response code {}:\n{:?}\n\n{}",
540                    code,
541                    headers,
542                    match body {
543                        Ok(body) => match String::from_utf8(body) {
544                            Ok(body) => body,
545                            Err(e) => format!("<Body was not UTF8: {:?}>", e),
546                        },
547                        Err(e) => format!("<Failed to read body: {}>", e),
548                    }
549                )))
550            }
551        }
552    }
553
554    async fn maps_get(
555        &self,
556        context: &C) -> Result<MapsGetResponse, ApiError>
557    {
558        let mut client_service = self.client_service.clone();
559        let mut uri = format!(
560            "{}/maps",
561            self.base_path
562        );
563
564        // Query parameters
565        let query_string = {
566            let mut query_string = form_urlencoded::Serializer::new("".to_owned());
567            query_string.finish()
568        };
569        if !query_string.is_empty() {
570            uri += "?";
571            uri += &query_string;
572        }
573
574        let uri = match Uri::from_str(&uri) {
575            Ok(uri) => uri,
576            Err(err) => return Err(ApiError(format!("Unable to build URI: {}", err))),
577        };
578
579        let mut request = match Request::builder()
580            .method("GET")
581            .uri(uri)
582            .body(Body::empty()) {
583                Ok(req) => req,
584                Err(e) => return Err(ApiError(format!("Unable to create request: {}", e)))
585        };
586
587        let header = HeaderValue::from_str(Has::<XSpanIdString>::get(context).0.as_str());
588        request.headers_mut().insert(HeaderName::from_static("x-span-id"), match header {
589            Ok(h) => h,
590            Err(e) => return Err(ApiError(format!("Unable to create X-Span ID header value: {}", e)))
591        });
592
593        let response = client_service.call((request, context.clone()))
594            .map_err(|e| ApiError(format!("No response received: {}", e))).await?;
595
596        match response.status().as_u16() {
597            200 => {
598                let body = response.into_body();
599                let body = body
600                        .into_raw()
601                        .map_err(|e| ApiError(format!("Failed to read response: {}", e))).await?;
602
603                let body = str::from_utf8(&body)
604                    .map_err(|e| ApiError(format!("Response was not valid UTF8: {}", e)))?;
605                let body = serde_json::from_str::<Vec<String>>(body)
606                    .map_err(|e| ApiError(format!("Response body did not match the schema: {}", e)))?;
607
608
609                Ok(MapsGetResponse::ListOfTheIdsOfAllExistingMaps
610                    (body)
611                )
612            }
613            500 => {
614                Ok(
615                    MapsGetResponse::NoMapsInDatabase
616                )
617            }
618            code => {
619                let headers = response.headers().clone();
620                let body = response.into_body()
621                       .take(100)
622                       .into_raw().await;
623                Err(ApiError(format!("Unexpected response code {}:\n{:?}\n\n{}",
624                    code,
625                    headers,
626                    match body {
627                        Ok(body) => match String::from_utf8(body) {
628                            Ok(body) => body,
629                            Err(e) => format!("<Body was not UTF8: {:?}>", e),
630                        },
631                        Err(e) => format!("<Failed to read body: {}>", e),
632                    }
633                )))
634            }
635        }
636    }
637
638    async fn register_post(
639        &self,
640        param_create_user_dto: models::CreateUserDto,
641        context: &C) -> Result<RegisterPostResponse, ApiError>
642    {
643        let mut client_service = self.client_service.clone();
644        let mut uri = format!(
645            "{}/register",
646            self.base_path
647        );
648
649        // Query parameters
650        let query_string = {
651            let mut query_string = form_urlencoded::Serializer::new("".to_owned());
652            query_string.finish()
653        };
654        if !query_string.is_empty() {
655            uri += "?";
656            uri += &query_string;
657        }
658
659        let uri = match Uri::from_str(&uri) {
660            Ok(uri) => uri,
661            Err(err) => return Err(ApiError(format!("Unable to build URI: {}", err))),
662        };
663
664        let mut request = match Request::builder()
665            .method("POST")
666            .uri(uri)
667            .body(Body::empty()) {
668                Ok(req) => req,
669                Err(e) => return Err(ApiError(format!("Unable to create request: {}", e)))
670        };
671
672        // Consumes basic body
673        // Body parameter
674        let body = serde_json::to_string(&param_create_user_dto).expect("impossible to fail to serialize");
675        *request.body_mut() = Body::from(body);
676
677        let header = "application/json";
678        request.headers_mut().insert(CONTENT_TYPE, match HeaderValue::from_str(header) {
679            Ok(h) => h,
680            Err(e) => return Err(ApiError(format!("Unable to create header: {} - {}", header, e)))
681        });
682
683        let header = HeaderValue::from_str(Has::<XSpanIdString>::get(context).0.as_str());
684        request.headers_mut().insert(HeaderName::from_static("x-span-id"), match header {
685            Ok(h) => h,
686            Err(e) => return Err(ApiError(format!("Unable to create X-Span ID header value: {}", e)))
687        });
688
689        let response = client_service.call((request, context.clone()))
690            .map_err(|e| ApiError(format!("No response received: {}", e))).await?;
691
692        match response.status().as_u16() {
693            200 => {
694                Ok(
695                    RegisterPostResponse::Created
696                )
697            }
698            409 => {
699                Ok(
700                    RegisterPostResponse::UserWithThatIDDoesAlreadyExist
701                )
702            }
703            code => {
704                let headers = response.headers().clone();
705                let body = response.into_body()
706                       .take(100)
707                       .into_raw().await;
708                Err(ApiError(format!("Unexpected response code {}:\n{:?}\n\n{}",
709                    code,
710                    headers,
711                    match body {
712                        Ok(body) => match String::from_utf8(body) {
713                            Ok(body) => body,
714                            Err(e) => format!("<Body was not UTF8: {:?}>", e),
715                        },
716                        Err(e) => format!("<Failed to read body: {}>", e),
717                    }
718                )))
719            }
720        }
721    }
722
723    async fn statistics_get(
724        &self,
725        context: &C) -> Result<StatisticsGetResponse, ApiError>
726    {
727        let mut client_service = self.client_service.clone();
728        let mut uri = format!(
729            "{}/statistics",
730            self.base_path
731        );
732
733        // Query parameters
734        let query_string = {
735            let mut query_string = form_urlencoded::Serializer::new("".to_owned());
736            query_string.finish()
737        };
738        if !query_string.is_empty() {
739            uri += "?";
740            uri += &query_string;
741        }
742
743        let uri = match Uri::from_str(&uri) {
744            Ok(uri) => uri,
745            Err(err) => return Err(ApiError(format!("Unable to build URI: {}", err))),
746        };
747
748        let mut request = match Request::builder()
749            .method("GET")
750            .uri(uri)
751            .body(Body::empty()) {
752                Ok(req) => req,
753                Err(e) => return Err(ApiError(format!("Unable to create request: {}", e)))
754        };
755
756        let header = HeaderValue::from_str(Has::<XSpanIdString>::get(context).0.as_str());
757        request.headers_mut().insert(HeaderName::from_static("x-span-id"), match header {
758            Ok(h) => h,
759            Err(e) => return Err(ApiError(format!("Unable to create X-Span ID header value: {}", e)))
760        });
761
762        let response = client_service.call((request, context.clone()))
763            .map_err(|e| ApiError(format!("No response received: {}", e))).await?;
764
765        match response.status().as_u16() {
766            200 => {
767                let body = response.into_body();
768                let body = body
769                        .into_raw()
770                        .map_err(|e| ApiError(format!("Failed to read response: {}", e))).await?;
771
772                let body = str::from_utf8(&body)
773                    .map_err(|e| ApiError(format!("Response was not valid UTF8: {}", e)))?;
774                let body = serde_json::from_str::<Vec<String>>(body)
775                    .map_err(|e| ApiError(format!("Response body did not match the schema: {}", e)))?;
776
777
778                Ok(StatisticsGetResponse::GameResultDetails
779                    (body)
780                )
781            }
782            code => {
783                let headers = response.headers().clone();
784                let body = response.into_body()
785                       .take(100)
786                       .into_raw().await;
787                Err(ApiError(format!("Unexpected response code {}:\n{:?}\n\n{}",
788                    code,
789                    headers,
790                    match body {
791                        Ok(body) => match String::from_utf8(body) {
792                            Ok(body) => body,
793                            Err(e) => format!("<Body was not UTF8: {:?}>", e),
794                        },
795                        Err(e) => format!("<Failed to read body: {}>", e),
796                    }
797                )))
798            }
799        }
800    }
801
802    async fn map_level_get(
803        &self,
804        param_level: String,
805        context: &C) -> Result<MapLevelGetResponse, ApiError>
806    {
807        let mut client_service = self.client_service.clone();
808        let mut uri = format!(
809            "{}/map/{level}",
810            self.base_path
811            ,level=utf8_percent_encode(&param_level.to_string(), ID_ENCODE_SET)
812        );
813
814        // Query parameters
815        let query_string = {
816            let mut query_string = form_urlencoded::Serializer::new("".to_owned());
817            query_string.finish()
818        };
819        if !query_string.is_empty() {
820            uri += "?";
821            uri += &query_string;
822        }
823
824        let uri = match Uri::from_str(&uri) {
825            Ok(uri) => uri,
826            Err(err) => return Err(ApiError(format!("Unable to build URI: {}", err))),
827        };
828
829        let mut request = match Request::builder()
830            .method("GET")
831            .uri(uri)
832            .body(Body::empty()) {
833                Ok(req) => req,
834                Err(e) => return Err(ApiError(format!("Unable to create request: {}", e)))
835        };
836
837        let header = HeaderValue::from_str(Has::<XSpanIdString>::get(context).0.as_str());
838        request.headers_mut().insert(HeaderName::from_static("x-span-id"), match header {
839            Ok(h) => h,
840            Err(e) => return Err(ApiError(format!("Unable to create X-Span ID header value: {}", e)))
841        });
842
843        let response = client_service.call((request, context.clone()))
844            .map_err(|e| ApiError(format!("No response received: {}", e))).await?;
845
846        match response.status().as_u16() {
847            200 => {
848                let body = response.into_body();
849                let body = body
850                        .into_raw()
851                        .map_err(|e| ApiError(format!("Failed to read response: {}", e))).await?;
852
853                let body = str::from_utf8(&body)
854                    .map_err(|e| ApiError(format!("Response was not valid UTF8: {}", e)))?;
855                let body = serde_json::from_str::<Vec<models::MapDto>>(body)
856                    .map_err(|e| ApiError(format!("Response body did not match the schema: {}", e)))?;
857
858
859                Ok(MapLevelGetResponse::BeatMap
860                    (body)
861                )
862            }
863            404 => {
864                Ok(
865                    MapLevelGetResponse::ThisMapDoesNotExist
866                )
867            }
868            code => {
869                let headers = response.headers().clone();
870                let body = response.into_body()
871                       .take(100)
872                       .into_raw().await;
873                Err(ApiError(format!("Unexpected response code {}:\n{:?}\n\n{}",
874                    code,
875                    headers,
876                    match body {
877                        Ok(body) => match String::from_utf8(body) {
878                            Ok(body) => body,
879                            Err(e) => format!("<Body was not UTF8: {:?}>", e),
880                        },
881                        Err(e) => format!("<Failed to read body: {}>", e),
882                    }
883                )))
884            }
885        }
886    }
887
888    async fn statistics_uuid_get(
889        &self,
890        param_uuid: String,
891        context: &C) -> Result<StatisticsUuidGetResponse, ApiError>
892    {
893        let mut client_service = self.client_service.clone();
894        let mut uri = format!(
895            "{}/statistics/{uuid}",
896            self.base_path
897            ,uuid=utf8_percent_encode(&param_uuid.to_string(), ID_ENCODE_SET)
898        );
899
900        // Query parameters
901        let query_string = {
902            let mut query_string = form_urlencoded::Serializer::new("".to_owned());
903            query_string.finish()
904        };
905        if !query_string.is_empty() {
906            uri += "?";
907            uri += &query_string;
908        }
909
910        let uri = match Uri::from_str(&uri) {
911            Ok(uri) => uri,
912            Err(err) => return Err(ApiError(format!("Unable to build URI: {}", err))),
913        };
914
915        let mut request = match Request::builder()
916            .method("GET")
917            .uri(uri)
918            .body(Body::empty()) {
919                Ok(req) => req,
920                Err(e) => return Err(ApiError(format!("Unable to create request: {}", e)))
921        };
922
923        let header = HeaderValue::from_str(Has::<XSpanIdString>::get(context).0.as_str());
924        request.headers_mut().insert(HeaderName::from_static("x-span-id"), match header {
925            Ok(h) => h,
926            Err(e) => return Err(ApiError(format!("Unable to create X-Span ID header value: {}", e)))
927        });
928
929        let response = client_service.call((request, context.clone()))
930            .map_err(|e| ApiError(format!("No response received: {}", e))).await?;
931
932        match response.status().as_u16() {
933            200 => {
934                let body = response.into_body();
935                let body = body
936                        .into_raw()
937                        .map_err(|e| ApiError(format!("Failed to read response: {}", e))).await?;
938
939                let body = str::from_utf8(&body)
940                    .map_err(|e| ApiError(format!("Response was not valid UTF8: {}", e)))?;
941                let body = serde_json::from_str::<models::ResultDto>(body)
942                    .map_err(|e| ApiError(format!("Response body did not match the schema: {}", e)))?;
943
944
945                Ok(StatisticsUuidGetResponse::GameResultDetails
946                    (body)
947                )
948            }
949            404 => {
950                Ok(
951                    StatisticsUuidGetResponse::ThisStatisticDoesNotExist
952                )
953            }
954            code => {
955                let headers = response.headers().clone();
956                let body = response.into_body()
957                       .take(100)
958                       .into_raw().await;
959                Err(ApiError(format!("Unexpected response code {}:\n{:?}\n\n{}",
960                    code,
961                    headers,
962                    match body {
963                        Ok(body) => match String::from_utf8(body) {
964                            Ok(body) => body,
965                            Err(e) => format!("<Body was not UTF8: {:?}>", e),
966                        },
967                        Err(e) => format!("<Failed to read body: {}>", e),
968                    }
969                )))
970            }
971        }
972    }
973
974    async fn user_uuid_get(
975        &self,
976        param_uuid: String,
977        context: &C) -> Result<UserUuidGetResponse, ApiError>
978    {
979        let mut client_service = self.client_service.clone();
980        let mut uri = format!(
981            "{}/user/{uuid}",
982            self.base_path
983            ,uuid=utf8_percent_encode(&param_uuid.to_string(), ID_ENCODE_SET)
984        );
985
986        // Query parameters
987        let query_string = {
988            let mut query_string = form_urlencoded::Serializer::new("".to_owned());
989            query_string.finish()
990        };
991        if !query_string.is_empty() {
992            uri += "?";
993            uri += &query_string;
994        }
995
996        let uri = match Uri::from_str(&uri) {
997            Ok(uri) => uri,
998            Err(err) => return Err(ApiError(format!("Unable to build URI: {}", err))),
999        };
1000
1001        let mut request = match Request::builder()
1002            .method("GET")
1003            .uri(uri)
1004            .body(Body::empty()) {
1005                Ok(req) => req,
1006                Err(e) => return Err(ApiError(format!("Unable to create request: {}", e)))
1007        };
1008
1009        let header = HeaderValue::from_str(Has::<XSpanIdString>::get(context).0.as_str());
1010        request.headers_mut().insert(HeaderName::from_static("x-span-id"), match header {
1011            Ok(h) => h,
1012            Err(e) => return Err(ApiError(format!("Unable to create X-Span ID header value: {}", e)))
1013        });
1014
1015        let response = client_service.call((request, context.clone()))
1016            .map_err(|e| ApiError(format!("No response received: {}", e))).await?;
1017
1018        match response.status().as_u16() {
1019            200 => {
1020                let body = response.into_body();
1021                let body = body
1022                        .into_raw()
1023                        .map_err(|e| ApiError(format!("Failed to read response: {}", e))).await?;
1024
1025                let body = str::from_utf8(&body)
1026                    .map_err(|e| ApiError(format!("Response was not valid UTF8: {}", e)))?;
1027                let body = serde_json::from_str::<models::UserDto>(body)
1028                    .map_err(|e| ApiError(format!("Response body did not match the schema: {}", e)))?;
1029
1030
1031                Ok(UserUuidGetResponse::SuccessfullyFetchedUserData
1032                    (body)
1033                )
1034            }
1035            404 => {
1036                Ok(
1037                    UserUuidGetResponse::UserWithThatIDDoesExist
1038                )
1039            }
1040            code => {
1041                let headers = response.headers().clone();
1042                let body = response.into_body()
1043                       .take(100)
1044                       .into_raw().await;
1045                Err(ApiError(format!("Unexpected response code {}:\n{:?}\n\n{}",
1046                    code,
1047                    headers,
1048                    match body {
1049                        Ok(body) => match String::from_utf8(body) {
1050                            Ok(body) => body,
1051                            Err(e) => format!("<Body was not UTF8: {:?}>", e),
1052                        },
1053                        Err(e) => format!("<Failed to read body: {}>", e),
1054                    }
1055                )))
1056            }
1057        }
1058    }
1059
1060}