tosca_controller/response.rs
1use tosca::response::{InfoResponse, OkResponse, SerialResponse};
2
3use reqwest::Response as ReqwestResponse;
4
5use serde::{Serialize, de::DeserializeOwned};
6
7use crate::error::{Error, ErrorKind, Result};
8
9// TODO:
10// OkCollector --> Save Ok responses in order to maintain a history.
11// SerialCollector --> Save serial responses in order to maintain a history.
12// InfoCollector --> Save Info responses in order to maintain a history.
13// StreamCollector --> Save information about a Stream Response before and after
14
15async fn json_response<T>(response: ReqwestResponse) -> Result<T>
16where
17 T: Serialize + DeserializeOwned,
18{
19 response
20 .json::<T>()
21 .await
22 .map_err(|e| Error::new(ErrorKind::JsonResponse, format!("Json error caused by {e}")))
23}
24
25/// An [`OkResponse`] body parser.
26pub struct OkResponseParser(ReqwestResponse);
27
28impl OkResponseParser {
29 /// Parses the internal response body to retrieve an [`OkResponse`].
30 ///
31 /// # Errors
32 ///
33 /// If the response body does not contain a valid [`OkResponse`], a
34 /// parsing error will be raised. This may occur due to an incorrect format
35 /// or because the binary data contains syntactic or semantic errors.
36 pub async fn parse_body(self) -> Result<OkResponse> {
37 json_response::<OkResponse>(self.0).await
38 }
39
40 pub(crate) const fn new(response: ReqwestResponse) -> Self {
41 Self(response)
42 }
43}
44
45/// A [`SerialResponse`] body parser.
46pub struct SerialResponseParser(ReqwestResponse);
47
48impl SerialResponseParser {
49 /// Parses the internal response body to retrieve a [`SerialResponse`].
50 ///
51 /// # Errors
52 ///
53 /// If the response body does not contain a valid [`SerialResponse`], a
54 /// parsing error will be raised. This may occur due to an incorrect format
55 /// or because the binary data contains syntactic or semantic errors.
56 pub async fn parse_body<T: Serialize + DeserializeOwned>(self) -> Result<SerialResponse<T>> {
57 json_response::<SerialResponse<T>>(self.0).await
58 }
59
60 pub(crate) const fn new(response: ReqwestResponse) -> Self {
61 Self(response)
62 }
63}
64
65/// An [`InfoResponse`] body parser.
66pub struct InfoResponseParser(ReqwestResponse);
67
68impl InfoResponseParser {
69 /// Parses the internal response body to retrieve an [`InfoResponse`].
70 ///
71 /// # Errors
72 ///
73 /// If the response body does not contain a valid [`InfoResponse`], a
74 /// parsing error will be raised. This may occur due to an incorrect format
75 /// or because the binary data contains syntactic or semantic errors.
76 pub async fn parse_body(self) -> Result<InfoResponse> {
77 json_response::<InfoResponse>(self.0).await
78 }
79
80 pub(crate) const fn new(response: ReqwestResponse) -> Self {
81 Self(response)
82 }
83}
84
85/// A byte stream response body parser.
86#[cfg(feature = "stream")]
87pub struct StreamResponse(ReqwestResponse);
88
89#[cfg(feature = "stream")]
90impl StreamResponse {
91 /// Opens a bytes stream from the response received from a device.
92 ///
93 /// # Errors
94 ///
95 /// Byte stream parsing may fail due to network errors or data corruption.
96 pub fn open_stream(self) -> impl futures_util::Stream<Item = Result<bytes::Bytes>> {
97 use futures_util::TryStreamExt;
98 self.0.bytes_stream().map_err(|e| {
99 Error::new(
100 ErrorKind::StreamResponse,
101 format!("Stream error caused by {e}"),
102 )
103 })
104 }
105
106 pub(crate) const fn new(response: ReqwestResponse) -> Self {
107 Self(response)
108 }
109}
110
111/// All response types supported by a `tosca` device.
112///
113/// Each response includes a dedicated body parser to extract the embedded data.
114pub enum Response {
115 /// A skipped response indicates a request that is not sent due to
116 /// privacy policy rules.
117 Skipped,
118 /// An [`OkResponse`] body.
119 OkBody(OkResponseParser),
120 /// A [`SerialResponse`] body.
121 SerialBody(SerialResponseParser),
122 /// An [`InfoResponse`] body.
123 InfoBody(InfoResponseParser),
124 /// A byte stream response body.
125 #[cfg(feature = "stream")]
126 StreamBody(StreamResponse),
127}