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#[allow(dead_code)]
28const FRAGMENT_ENCODE_SET: &AsciiSet = &percent_encoding::CONTROLS
29 .add(b' ').add(b'"').add(b'<').add(b'>').add(b'`');
30
31#[allow(dead_code)]
36const ID_ENCODE_SET: &AsciiSet = &FRAGMENT_ENCODE_SET.add(b'|');
37
38use crate::{Api,
39 GetResponse,
40 GetMultiResponse,
41 HatResponse,
42 HatOffResponse,
43 HatOnResponse,
44 MbusApiResponse,
45 ScanResponse
46 };
47
48fn into_base_path(input: impl TryInto<Uri, Error=hyper::http::uri::InvalidUri>, correct_scheme: Option<&'static str>) -> Result<String, ClientInitError> {
50 let uri = input.try_into()?;
52
53 let scheme = uri.scheme_str().ok_or(ClientInitError::InvalidScheme)?;
54
55 if let Some(correct_scheme) = correct_scheme {
57 if scheme != correct_scheme {
58 return Err(ClientInitError::InvalidScheme);
59 }
60 }
61
62 let host = uri.host().ok_or_else(|| ClientInitError::MissingHost)?;
63 let port = uri.port_u16().map(|x| format!(":{}", x)).unwrap_or_default();
64 Ok(format!("{}://{}{}{}", scheme, host, port, uri.path().trim_end_matches('/')))
65}
66
67pub struct Client<S, C> where
69 S: Service<
70 (Request<Body>, C),
71 Response=Response<Body>> + Clone + Sync + Send + 'static,
72 S::Future: Send + 'static,
73 S::Error: Into<crate::ServiceError> + fmt::Display,
74 C: Clone + Send + Sync + 'static
75{
76 client_service: S,
78
79 base_path: String,
81
82 marker: PhantomData<fn(C)>,
84}
85
86impl<S, C> fmt::Debug for Client<S, C> where
87 S: Service<
88 (Request<Body>, C),
89 Response=Response<Body>> + Clone + Sync + Send + 'static,
90 S::Future: Send + 'static,
91 S::Error: Into<crate::ServiceError> + fmt::Display,
92 C: Clone + Send + Sync + 'static
93{
94 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
95 write!(f, "Client {{ base_path: {} }}", self.base_path)
96 }
97}
98
99impl<S, C> Clone for Client<S, C> where
100 S: Service<
101 (Request<Body>, C),
102 Response=Response<Body>> + Clone + Sync + Send + 'static,
103 S::Future: Send + 'static,
104 S::Error: Into<crate::ServiceError> + fmt::Display,
105 C: Clone + Send + Sync + 'static
106{
107 fn clone(&self) -> Self {
108 Self {
109 client_service: self.client_service.clone(),
110 base_path: self.base_path.clone(),
111 marker: PhantomData,
112 }
113 }
114}
115
116impl<Connector, C> Client<DropContextService<hyper::client::Client<Connector, Body>, C>, C> where
117 Connector: hyper::client::connect::Connect + Clone + Send + Sync + 'static,
118 C: Clone + Send + Sync + 'static,
119{
120 pub fn try_new_with_connector(
135 base_path: &str,
136 protocol: Option<&'static str>,
137 connector: Connector,
138 ) -> Result<Self, ClientInitError>
139 {
140 let client_service = hyper::client::Client::builder().build(connector);
141 let client_service = DropContextService::new(client_service);
142
143 Ok(Self {
144 client_service,
145 base_path: into_base_path(base_path, protocol)?,
146 marker: PhantomData,
147 })
148 }
149}
150
151#[derive(Debug, Clone)]
152pub enum HyperClient {
153 Http(hyper::client::Client<hyper::client::HttpConnector, Body>),
154 Https(hyper::client::Client<HttpsConnector, Body>),
155}
156
157impl Service<Request<Body>> for HyperClient {
158 type Response = Response<Body>;
159 type Error = hyper::Error;
160 type Future = hyper::client::ResponseFuture;
161
162 fn poll_ready(&mut self, cx: &mut Context) -> Poll<Result<(), Self::Error>> {
163 match self {
164 HyperClient::Http(client) => client.poll_ready(cx),
165 HyperClient::Https(client) => client.poll_ready(cx),
166 }
167 }
168
169 fn call(&mut self, req: Request<Body>) -> Self::Future {
170 match self {
171 HyperClient::Http(client) => client.call(req),
172 HyperClient::Https(client) => client.call(req)
173 }
174 }
175}
176
177impl<C> Client<DropContextService<HyperClient, C>, C> where
178 C: Clone + Send + Sync + 'static,
179{
180 pub fn try_new(
185 base_path: &str,
186 ) -> Result<Self, ClientInitError> {
187 let uri = Uri::from_str(base_path)?;
188
189 let scheme = uri.scheme_str().ok_or(ClientInitError::InvalidScheme)?;
190 let scheme = scheme.to_ascii_lowercase();
191
192 let connector = Connector::builder();
193
194 let client_service = match scheme.as_str() {
195 "http" => {
196 HyperClient::Http(hyper::client::Client::builder().build(connector.build()))
197 },
198 "https" => {
199 let connector = connector.https()
200 .build()
201 .map_err(|e| ClientInitError::SslError(e))?;
202 HyperClient::Https(hyper::client::Client::builder().build(connector))
203 },
204 _ => {
205 return Err(ClientInitError::InvalidScheme);
206 }
207 };
208
209 let client_service = DropContextService::new(client_service);
210
211 Ok(Self {
212 client_service,
213 base_path: into_base_path(base_path, None)?,
214 marker: PhantomData,
215 })
216 }
217}
218
219impl<C> Client<DropContextService<hyper::client::Client<hyper::client::HttpConnector, Body>, C>, C> where
220 C: Clone + Send + Sync + 'static
221{
222 pub fn try_new_http(
227 base_path: &str,
228 ) -> Result<Self, ClientInitError> {
229 let http_connector = Connector::builder().build();
230
231 Self::try_new_with_connector(base_path, Some("http"), http_connector)
232 }
233}
234
235#[cfg(any(target_os = "macos", target_os = "windows", target_os = "ios"))]
236type HttpsConnector = hyper_tls::HttpsConnector<hyper::client::HttpConnector>;
237
238#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
239type HttpsConnector = hyper_openssl::HttpsConnector<hyper::client::HttpConnector>;
240
241impl<C> Client<DropContextService<hyper::client::Client<HttpsConnector, Body>, C>, C> where
242 C: Clone + Send + Sync + 'static
243{
244 pub fn try_new_https(base_path: &str) -> Result<Self, ClientInitError>
249 {
250 let https_connector = Connector::builder()
251 .https()
252 .build()
253 .map_err(|e| ClientInitError::SslError(e))?;
254 Self::try_new_with_connector(base_path, Some("https"), https_connector)
255 }
256
257 #[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
263 pub fn try_new_https_pinned<CA>(
264 base_path: &str,
265 ca_certificate: CA,
266 ) -> Result<Self, ClientInitError>
267 where
268 CA: AsRef<Path>,
269 {
270 let https_connector = Connector::builder()
271 .https()
272 .pin_server_certificate(ca_certificate)
273 .build()
274 .map_err(|e| ClientInitError::SslError(e))?;
275 Self::try_new_with_connector(base_path, Some("https"), https_connector)
276 }
277
278 #[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
286 pub fn try_new_https_mutual<CA, K, D>(
287 base_path: &str,
288 ca_certificate: CA,
289 client_key: K,
290 client_certificate: D,
291 ) -> Result<Self, ClientInitError>
292 where
293 CA: AsRef<Path>,
294 K: AsRef<Path>,
295 D: AsRef<Path>,
296 {
297 let https_connector = Connector::builder()
298 .https()
299 .pin_server_certificate(ca_certificate)
300 .client_authentication(client_key, client_certificate)
301 .build()
302 .map_err(|e| ClientInitError::SslError(e))?;
303 Self::try_new_with_connector(base_path, Some("https"), https_connector)
304 }
305}
306
307impl<S, C> Client<S, C> where
308 S: Service<
309 (Request<Body>, C),
310 Response=Response<Body>> + Clone + Sync + Send + 'static,
311 S::Future: Send + 'static,
312 S::Error: Into<crate::ServiceError> + fmt::Display,
313 C: Clone + Send + Sync + 'static
314{
315 pub fn try_new_with_client_service(
320 client_service: S,
321 base_path: &str,
322 ) -> Result<Self, ClientInitError>
323 {
324 Ok(Self {
325 client_service,
326 base_path: into_base_path(base_path, None)?,
327 marker: PhantomData,
328 })
329 }
330}
331
332#[derive(Debug)]
334pub enum ClientInitError {
335 InvalidScheme,
337
338 InvalidUri(hyper::http::uri::InvalidUri),
340
341 MissingHost,
343
344 #[cfg(any(target_os = "macos", target_os = "windows", target_os = "ios"))]
346 SslError(native_tls::Error),
347
348 #[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
350 SslError(openssl::error::ErrorStack),
351}
352
353impl From<hyper::http::uri::InvalidUri> for ClientInitError {
354 fn from(err: hyper::http::uri::InvalidUri) -> ClientInitError {
355 ClientInitError::InvalidUri(err)
356 }
357}
358
359impl fmt::Display for ClientInitError {
360 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
361 let s: &dyn fmt::Debug = self;
362 s.fmt(f)
363 }
364}
365
366impl Error for ClientInitError {
367 fn description(&self) -> &str {
368 "Failed to produce a hyper client."
369 }
370}
371
372#[async_trait]
373impl<S, C> Api<C> for Client<S, C> where
374 S: Service<
375 (Request<Body>, C),
376 Response=Response<Body>> + Clone + Sync + Send + 'static,
377 S::Future: Send + 'static,
378 S::Error: Into<crate::ServiceError> + fmt::Display,
379 C: Has<XSpanIdString> + Clone + Send + Sync + 'static,
380{
381 fn poll_ready(&self, cx: &mut Context) -> Poll<Result<(), crate::ServiceError>> {
382 match self.client_service.clone().poll_ready(cx) {
383 Poll::Ready(Err(e)) => Poll::Ready(Err(e.into())),
384 Poll::Ready(Ok(o)) => Poll::Ready(Ok(o)),
385 Poll::Pending => Poll::Pending,
386 }
387 }
388
389 async fn get(
390 &self,
391 param_device: String,
392 param_baudrate: models::Baudrate,
393 param_address: String,
394 context: &C) -> Result<GetResponse, ApiError>
395 {
396 let mut client_service = self.client_service.clone();
397 let mut uri = format!(
398 "{}/mbus/get/{device}/{baudrate}/{address}",
399 self.base_path
400 ,device=utf8_percent_encode(¶m_device.to_string(), ID_ENCODE_SET)
401 ,baudrate=utf8_percent_encode(¶m_baudrate.to_string(), ID_ENCODE_SET)
402 ,address=utf8_percent_encode(¶m_address.to_string(), ID_ENCODE_SET)
403 );
404
405 let query_string = {
407 let mut query_string = form_urlencoded::Serializer::new("".to_owned());
408 query_string.finish()
409 };
410 if !query_string.is_empty() {
411 uri += "?";
412 uri += &query_string;
413 }
414
415 let uri = match Uri::from_str(&uri) {
416 Ok(uri) => uri,
417 Err(err) => return Err(ApiError(format!("Unable to build URI: {}", err))),
418 };
419
420 let mut request = match Request::builder()
421 .method("POST")
422 .uri(uri)
423 .body(Body::empty()) {
424 Ok(req) => req,
425 Err(e) => return Err(ApiError(format!("Unable to create request: {}", e)))
426 };
427
428 let header = HeaderValue::from_str(Has::<XSpanIdString>::get(context).0.clone().to_string().as_str());
429 request.headers_mut().insert(HeaderName::from_static("x-span-id"), match header {
430 Ok(h) => h,
431 Err(e) => return Err(ApiError(format!("Unable to create X-Span ID header value: {}", e)))
432 });
433
434 let mut response = client_service.call((request, context.clone()))
435 .map_err(|e| ApiError(format!("No response received: {}", e))).await?;
436
437 match response.status().as_u16() {
438 200 => {
439 let body = response.into_body();
440 let body = body
441 .to_raw()
442 .map_err(|e| ApiError(format!("Failed to read response: {}", e))).await?;
443 let body = str::from_utf8(&body)
444 .map_err(|e| ApiError(format!("Response was not valid UTF8: {}", e)))?;
445 let body = serde_xml_rs::from_str::<String>(body)
448 .map_err(|e| ApiError(format!("Response body did not match the schema: {}", e)))?;
449 Ok(GetResponse::OK
450 (body)
451 )
452 }
453 400 => {
454 let body = response.into_body();
455 let body = body
456 .to_raw()
457 .map_err(|e| ApiError(format!("Failed to read response: {}", e))).await?;
458 let body = str::from_utf8(&body)
459 .map_err(|e| ApiError(format!("Response was not valid UTF8: {}", e)))?;
460 let body = body.to_string();
461 Ok(GetResponse::BadRequest
462 (body)
463 )
464 }
465 404 => {
466 let body = response.into_body();
467 let body = body
468 .to_raw()
469 .map_err(|e| ApiError(format!("Failed to read response: {}", e))).await?;
470 let body = str::from_utf8(&body)
471 .map_err(|e| ApiError(format!("Response was not valid UTF8: {}", e)))?;
472 let body = body.to_string();
473 Ok(GetResponse::NotFound
474 (body)
475 )
476 }
477 code => {
478 let headers = response.headers().clone();
479 let body = response.into_body()
480 .take(100)
481 .to_raw().await;
482 Err(ApiError(format!("Unexpected response code {}:\n{:?}\n\n{}",
483 code,
484 headers,
485 match body {
486 Ok(body) => match String::from_utf8(body) {
487 Ok(body) => body,
488 Err(e) => format!("<Body was not UTF8: {:?}>", e),
489 },
490 Err(e) => format!("<Failed to read body: {}>", e),
491 }
492 )))
493 }
494 }
495 }
496
497 async fn get_multi(
498 &self,
499 param_device: String,
500 param_baudrate: models::Baudrate,
501 param_address: String,
502 param_maxframes: i32,
503 context: &C) -> Result<GetMultiResponse, ApiError>
504 {
505 let mut client_service = self.client_service.clone();
506 let mut uri = format!(
507 "{}/mbus/getMulti/{device}/{baudrate}/{address}/{maxframes}",
508 self.base_path
509 ,device=utf8_percent_encode(¶m_device.to_string(), ID_ENCODE_SET)
510 ,baudrate=utf8_percent_encode(¶m_baudrate.to_string(), ID_ENCODE_SET)
511 ,address=utf8_percent_encode(¶m_address.to_string(), ID_ENCODE_SET)
512 ,maxframes=utf8_percent_encode(¶m_maxframes.to_string(), ID_ENCODE_SET)
513 );
514
515 let query_string = {
517 let mut query_string = form_urlencoded::Serializer::new("".to_owned());
518 query_string.finish()
519 };
520 if !query_string.is_empty() {
521 uri += "?";
522 uri += &query_string;
523 }
524
525 let uri = match Uri::from_str(&uri) {
526 Ok(uri) => uri,
527 Err(err) => return Err(ApiError(format!("Unable to build URI: {}", err))),
528 };
529
530 let mut request = match Request::builder()
531 .method("POST")
532 .uri(uri)
533 .body(Body::empty()) {
534 Ok(req) => req,
535 Err(e) => return Err(ApiError(format!("Unable to create request: {}", e)))
536 };
537
538 let header = HeaderValue::from_str(Has::<XSpanIdString>::get(context).0.clone().to_string().as_str());
539 request.headers_mut().insert(HeaderName::from_static("x-span-id"), match header {
540 Ok(h) => h,
541 Err(e) => return Err(ApiError(format!("Unable to create X-Span ID header value: {}", e)))
542 });
543
544 let mut response = client_service.call((request, context.clone()))
545 .map_err(|e| ApiError(format!("No response received: {}", e))).await?;
546
547 match response.status().as_u16() {
548 200 => {
549 let body = response.into_body();
550 let body = body
551 .to_raw()
552 .map_err(|e| ApiError(format!("Failed to read response: {}", e))).await?;
553 let body = str::from_utf8(&body)
554 .map_err(|e| ApiError(format!("Response was not valid UTF8: {}", e)))?;
555 let body = serde_xml_rs::from_str::<String>(body)
558 .map_err(|e| ApiError(format!("Response body did not match the schema: {}", e)))?;
559 Ok(GetMultiResponse::OK
560 (body)
561 )
562 }
563 400 => {
564 let body = response.into_body();
565 let body = body
566 .to_raw()
567 .map_err(|e| ApiError(format!("Failed to read response: {}", e))).await?;
568 let body = str::from_utf8(&body)
569 .map_err(|e| ApiError(format!("Response was not valid UTF8: {}", e)))?;
570 let body = body.to_string();
571 Ok(GetMultiResponse::BadRequest
572 (body)
573 )
574 }
575 404 => {
576 let body = response.into_body();
577 let body = body
578 .to_raw()
579 .map_err(|e| ApiError(format!("Failed to read response: {}", e))).await?;
580 let body = str::from_utf8(&body)
581 .map_err(|e| ApiError(format!("Response was not valid UTF8: {}", e)))?;
582 let body = body.to_string();
583 Ok(GetMultiResponse::NotFound
584 (body)
585 )
586 }
587 code => {
588 let headers = response.headers().clone();
589 let body = response.into_body()
590 .take(100)
591 .to_raw().await;
592 Err(ApiError(format!("Unexpected response code {}:\n{:?}\n\n{}",
593 code,
594 headers,
595 match body {
596 Ok(body) => match String::from_utf8(body) {
597 Ok(body) => body,
598 Err(e) => format!("<Body was not UTF8: {:?}>", e),
599 },
600 Err(e) => format!("<Failed to read body: {}>", e),
601 }
602 )))
603 }
604 }
605 }
606
607 async fn hat(
608 &self,
609 context: &C) -> Result<HatResponse, ApiError>
610 {
611 let mut client_service = self.client_service.clone();
612 let mut uri = format!(
613 "{}/mbus/hat",
614 self.base_path
615 );
616
617 let query_string = {
619 let mut query_string = form_urlencoded::Serializer::new("".to_owned());
620 query_string.finish()
621 };
622 if !query_string.is_empty() {
623 uri += "?";
624 uri += &query_string;
625 }
626
627 let uri = match Uri::from_str(&uri) {
628 Ok(uri) => uri,
629 Err(err) => return Err(ApiError(format!("Unable to build URI: {}", err))),
630 };
631
632 let mut request = match Request::builder()
633 .method("GET")
634 .uri(uri)
635 .body(Body::empty()) {
636 Ok(req) => req,
637 Err(e) => return Err(ApiError(format!("Unable to create request: {}", e)))
638 };
639
640 let header = HeaderValue::from_str(Has::<XSpanIdString>::get(context).0.clone().to_string().as_str());
641 request.headers_mut().insert(HeaderName::from_static("x-span-id"), match header {
642 Ok(h) => h,
643 Err(e) => return Err(ApiError(format!("Unable to create X-Span ID header value: {}", e)))
644 });
645
646 let mut response = client_service.call((request, context.clone()))
647 .map_err(|e| ApiError(format!("No response received: {}", e))).await?;
648
649 match response.status().as_u16() {
650 200 => {
651 let body = response.into_body();
652 let body = body
653 .to_raw()
654 .map_err(|e| ApiError(format!("Failed to read response: {}", e))).await?;
655 let body = str::from_utf8(&body)
656 .map_err(|e| ApiError(format!("Response was not valid UTF8: {}", e)))?;
657 let body = serde_json::from_str::<models::Hat>(body)?;
658 Ok(HatResponse::OK
659 (body)
660 )
661 }
662 404 => {
663 let body = response.into_body();
664 let body = body
665 .to_raw()
666 .map_err(|e| ApiError(format!("Failed to read response: {}", e))).await?;
667 let body = str::from_utf8(&body)
668 .map_err(|e| ApiError(format!("Response was not valid UTF8: {}", e)))?;
669 let body = body.to_string();
670 Ok(HatResponse::NotFound
671 (body)
672 )
673 }
674 code => {
675 let headers = response.headers().clone();
676 let body = response.into_body()
677 .take(100)
678 .to_raw().await;
679 Err(ApiError(format!("Unexpected response code {}:\n{:?}\n\n{}",
680 code,
681 headers,
682 match body {
683 Ok(body) => match String::from_utf8(body) {
684 Ok(body) => body,
685 Err(e) => format!("<Body was not UTF8: {:?}>", e),
686 },
687 Err(e) => format!("<Failed to read body: {}>", e),
688 }
689 )))
690 }
691 }
692 }
693
694 async fn hat_off(
695 &self,
696 context: &C) -> Result<HatOffResponse, ApiError>
697 {
698 let mut client_service = self.client_service.clone();
699 let mut uri = format!(
700 "{}/mbus/hat/off",
701 self.base_path
702 );
703
704 let query_string = {
706 let mut query_string = form_urlencoded::Serializer::new("".to_owned());
707 query_string.finish()
708 };
709 if !query_string.is_empty() {
710 uri += "?";
711 uri += &query_string;
712 }
713
714 let uri = match Uri::from_str(&uri) {
715 Ok(uri) => uri,
716 Err(err) => return Err(ApiError(format!("Unable to build URI: {}", err))),
717 };
718
719 let mut request = match Request::builder()
720 .method("POST")
721 .uri(uri)
722 .body(Body::empty()) {
723 Ok(req) => req,
724 Err(e) => return Err(ApiError(format!("Unable to create request: {}", e)))
725 };
726
727 let header = HeaderValue::from_str(Has::<XSpanIdString>::get(context).0.clone().to_string().as_str());
728 request.headers_mut().insert(HeaderName::from_static("x-span-id"), match header {
729 Ok(h) => h,
730 Err(e) => return Err(ApiError(format!("Unable to create X-Span ID header value: {}", e)))
731 });
732
733 let mut response = client_service.call((request, context.clone()))
734 .map_err(|e| ApiError(format!("No response received: {}", e))).await?;
735
736 match response.status().as_u16() {
737 200 => {
738 let body = response.into_body();
739 Ok(
740 HatOffResponse::OK
741 )
742 }
743 404 => {
744 let body = response.into_body();
745 let body = body
746 .to_raw()
747 .map_err(|e| ApiError(format!("Failed to read response: {}", e))).await?;
748 let body = str::from_utf8(&body)
749 .map_err(|e| ApiError(format!("Response was not valid UTF8: {}", e)))?;
750 let body = body.to_string();
751 Ok(HatOffResponse::NotFound
752 (body)
753 )
754 }
755 code => {
756 let headers = response.headers().clone();
757 let body = response.into_body()
758 .take(100)
759 .to_raw().await;
760 Err(ApiError(format!("Unexpected response code {}:\n{:?}\n\n{}",
761 code,
762 headers,
763 match body {
764 Ok(body) => match String::from_utf8(body) {
765 Ok(body) => body,
766 Err(e) => format!("<Body was not UTF8: {:?}>", e),
767 },
768 Err(e) => format!("<Failed to read body: {}>", e),
769 }
770 )))
771 }
772 }
773 }
774
775 async fn hat_on(
776 &self,
777 context: &C) -> Result<HatOnResponse, ApiError>
778 {
779 let mut client_service = self.client_service.clone();
780 let mut uri = format!(
781 "{}/mbus/hat/on",
782 self.base_path
783 );
784
785 let query_string = {
787 let mut query_string = form_urlencoded::Serializer::new("".to_owned());
788 query_string.finish()
789 };
790 if !query_string.is_empty() {
791 uri += "?";
792 uri += &query_string;
793 }
794
795 let uri = match Uri::from_str(&uri) {
796 Ok(uri) => uri,
797 Err(err) => return Err(ApiError(format!("Unable to build URI: {}", err))),
798 };
799
800 let mut request = match Request::builder()
801 .method("POST")
802 .uri(uri)
803 .body(Body::empty()) {
804 Ok(req) => req,
805 Err(e) => return Err(ApiError(format!("Unable to create request: {}", e)))
806 };
807
808 let header = HeaderValue::from_str(Has::<XSpanIdString>::get(context).0.clone().to_string().as_str());
809 request.headers_mut().insert(HeaderName::from_static("x-span-id"), match header {
810 Ok(h) => h,
811 Err(e) => return Err(ApiError(format!("Unable to create X-Span ID header value: {}", e)))
812 });
813
814 let mut response = client_service.call((request, context.clone()))
815 .map_err(|e| ApiError(format!("No response received: {}", e))).await?;
816
817 match response.status().as_u16() {
818 200 => {
819 let body = response.into_body();
820 Ok(
821 HatOnResponse::OK
822 )
823 }
824 404 => {
825 let body = response.into_body();
826 let body = body
827 .to_raw()
828 .map_err(|e| ApiError(format!("Failed to read response: {}", e))).await?;
829 let body = str::from_utf8(&body)
830 .map_err(|e| ApiError(format!("Response was not valid UTF8: {}", e)))?;
831 let body = body.to_string();
832 Ok(HatOnResponse::NotFound
833 (body)
834 )
835 }
836 code => {
837 let headers = response.headers().clone();
838 let body = response.into_body()
839 .take(100)
840 .to_raw().await;
841 Err(ApiError(format!("Unexpected response code {}:\n{:?}\n\n{}",
842 code,
843 headers,
844 match body {
845 Ok(body) => match String::from_utf8(body) {
846 Ok(body) => body,
847 Err(e) => format!("<Body was not UTF8: {:?}>", e),
848 },
849 Err(e) => format!("<Failed to read body: {}>", e),
850 }
851 )))
852 }
853 }
854 }
855
856 async fn mbus_api(
857 &self,
858 context: &C) -> Result<MbusApiResponse, ApiError>
859 {
860 let mut client_service = self.client_service.clone();
861 let mut uri = format!(
862 "{}/mbus/api",
863 self.base_path
864 );
865
866 let query_string = {
868 let mut query_string = form_urlencoded::Serializer::new("".to_owned());
869 query_string.finish()
870 };
871 if !query_string.is_empty() {
872 uri += "?";
873 uri += &query_string;
874 }
875
876 let uri = match Uri::from_str(&uri) {
877 Ok(uri) => uri,
878 Err(err) => return Err(ApiError(format!("Unable to build URI: {}", err))),
879 };
880
881 let mut request = match Request::builder()
882 .method("GET")
883 .uri(uri)
884 .body(Body::empty()) {
885 Ok(req) => req,
886 Err(e) => return Err(ApiError(format!("Unable to create request: {}", e)))
887 };
888
889 let header = HeaderValue::from_str(Has::<XSpanIdString>::get(context).0.clone().to_string().as_str());
890 request.headers_mut().insert(HeaderName::from_static("x-span-id"), match header {
891 Ok(h) => h,
892 Err(e) => return Err(ApiError(format!("Unable to create X-Span ID header value: {}", e)))
893 });
894
895 let mut response = client_service.call((request, context.clone()))
896 .map_err(|e| ApiError(format!("No response received: {}", e))).await?;
897
898 match response.status().as_u16() {
899 200 => {
900 let body = response.into_body();
901 let body = body
902 .to_raw()
903 .map_err(|e| ApiError(format!("Failed to read response: {}", e))).await?;
904 let body = str::from_utf8(&body)
905 .map_err(|e| ApiError(format!("Response was not valid UTF8: {}", e)))?;
906 let body = body.to_string();
907 Ok(MbusApiResponse::OK
908 (body)
909 )
910 }
911 404 => {
912 let body = response.into_body();
913 let body = body
914 .to_raw()
915 .map_err(|e| ApiError(format!("Failed to read response: {}", e))).await?;
916 let body = str::from_utf8(&body)
917 .map_err(|e| ApiError(format!("Response was not valid UTF8: {}", e)))?;
918 let body = body.to_string();
919 Ok(MbusApiResponse::NotFound
920 (body)
921 )
922 }
923 code => {
924 let headers = response.headers().clone();
925 let body = response.into_body()
926 .take(100)
927 .to_raw().await;
928 Err(ApiError(format!("Unexpected response code {}:\n{:?}\n\n{}",
929 code,
930 headers,
931 match body {
932 Ok(body) => match String::from_utf8(body) {
933 Ok(body) => body,
934 Err(e) => format!("<Body was not UTF8: {:?}>", e),
935 },
936 Err(e) => format!("<Failed to read body: {}>", e),
937 }
938 )))
939 }
940 }
941 }
942
943 async fn scan(
944 &self,
945 param_device: String,
946 param_baudrate: models::Baudrate,
947 context: &C) -> Result<ScanResponse, ApiError>
948 {
949 let mut client_service = self.client_service.clone();
950 let mut uri = format!(
951 "{}/mbus/scan/{device}/{baudrate}",
952 self.base_path
953 ,device=utf8_percent_encode(¶m_device.to_string(), ID_ENCODE_SET)
954 ,baudrate=utf8_percent_encode(¶m_baudrate.to_string(), ID_ENCODE_SET)
955 );
956
957 let query_string = {
959 let mut query_string = form_urlencoded::Serializer::new("".to_owned());
960 query_string.finish()
961 };
962 if !query_string.is_empty() {
963 uri += "?";
964 uri += &query_string;
965 }
966
967 let uri = match Uri::from_str(&uri) {
968 Ok(uri) => uri,
969 Err(err) => return Err(ApiError(format!("Unable to build URI: {}", err))),
970 };
971
972 let mut request = match Request::builder()
973 .method("POST")
974 .uri(uri)
975 .body(Body::empty()) {
976 Ok(req) => req,
977 Err(e) => return Err(ApiError(format!("Unable to create request: {}", e)))
978 };
979
980 let header = HeaderValue::from_str(Has::<XSpanIdString>::get(context).0.clone().to_string().as_str());
981 request.headers_mut().insert(HeaderName::from_static("x-span-id"), match header {
982 Ok(h) => h,
983 Err(e) => return Err(ApiError(format!("Unable to create X-Span ID header value: {}", e)))
984 });
985
986 let mut response = client_service.call((request, context.clone()))
987 .map_err(|e| ApiError(format!("No response received: {}", e))).await?;
988
989 match response.status().as_u16() {
990 200 => {
991 let body = response.into_body();
992 let body = body
993 .to_raw()
994 .map_err(|e| ApiError(format!("Failed to read response: {}", e))).await?;
995 let body = str::from_utf8(&body)
996 .map_err(|e| ApiError(format!("Response was not valid UTF8: {}", e)))?;
997 let body = body.to_string();
998 Ok(ScanResponse::OK
999 (body)
1000 )
1001 }
1002 400 => {
1003 let body = response.into_body();
1004 let body = body
1005 .to_raw()
1006 .map_err(|e| ApiError(format!("Failed to read response: {}", e))).await?;
1007 let body = str::from_utf8(&body)
1008 .map_err(|e| ApiError(format!("Response was not valid UTF8: {}", e)))?;
1009 let body = body.to_string();
1010 Ok(ScanResponse::BadRequest
1011 (body)
1012 )
1013 }
1014 404 => {
1015 let body = response.into_body();
1016 let body = body
1017 .to_raw()
1018 .map_err(|e| ApiError(format!("Failed to read response: {}", e))).await?;
1019 let body = str::from_utf8(&body)
1020 .map_err(|e| ApiError(format!("Response was not valid UTF8: {}", e)))?;
1021 let body = body.to_string();
1022 Ok(ScanResponse::NotFound
1023 (body)
1024 )
1025 }
1026 code => {
1027 let headers = response.headers().clone();
1028 let body = response.into_body()
1029 .take(100)
1030 .to_raw().await;
1031 Err(ApiError(format!("Unexpected response code {}:\n{:?}\n\n{}",
1032 code,
1033 headers,
1034 match body {
1035 Ok(body) => match String::from_utf8(body) {
1036 Ok(body) => body,
1037 Err(e) => format!("<Body was not UTF8: {:?}>", e),
1038 },
1039 Err(e) => format!("<Failed to read body: {}>", e),
1040 }
1041 )))
1042 }
1043 }
1044 }
1045
1046}