exc_binance/http/response/
mod.rs1use super::error::RestError;
2use anyhow::anyhow;
3use either::Either;
4use exc_core::ExchangeError;
5use http::StatusCode;
6use serde::{de::DeserializeOwned, Deserialize};
7
8pub mod instrument;
10
11pub mod candle;
13
14pub mod listen_key;
16
17pub mod error_message;
19
20pub mod trading;
22
23pub mod account;
25
26pub use self::{
27 account::{
28 SubAccountBalances, SubAccountFutures, SubAccountFuturesPositions, SubAccountMargin,
29 SubAccounts,
30 },
31 candle::Candle,
32 error_message::ErrorMessage,
33 instrument::{ExchangeInfo, SpotExchangeInfo, UFExchangeInfo},
34 listen_key::ListenKey,
35 trading::Order,
36};
37
38pub type Candles = Vec<Candle>;
40
41pub type Unknown = serde_json::Value;
43
44#[allow(clippy::large_enum_variant)]
46#[derive(Debug, Deserialize)]
47#[serde(untagged)]
48pub enum Data {
49 Candles(Vec<Candle>),
51 OptionsCandles(Vec<candle::OptionsCandle>),
53 ExchangeInfo(ExchangeInfo),
55 ListenKey(ListenKey),
57 Error(ErrorMessage),
59 Order(Order),
61 SubAccounts(SubAccounts),
63 SubAccountBalances(SubAccountBalances),
65 SubAccountMargin(SubAccountMargin),
67 SubAccountFutures(SubAccountFutures),
69 SubAccountFuturesPositions(SubAccountFuturesPositions),
71 Unknwon(Unknown),
73}
74
75impl TryFrom<Data> for Unknown {
76 type Error = RestError;
77
78 fn try_from(value: Data) -> Result<Self, Self::Error> {
79 match value {
80 Data::Unknwon(u) => Ok(u),
81 _ => Err(RestError::UnexpectedResponseType(anyhow::anyhow!(
82 "{value:?}"
83 ))),
84 }
85 }
86}
87
88#[derive(Debug)]
90pub struct RestResponse<T> {
91 data: T,
92}
93
94impl<T> RestResponse<T> {
95 pub fn into_inner(self) -> T {
97 self.data
98 }
99
100 pub fn into_response<U>(self) -> Result<U, RestError>
102 where
103 U: TryFrom<T, Error = RestError>,
104 {
105 U::try_from(self.into_inner())
106 }
107
108 pub(crate) async fn from_http(resp: http::Response<hyper::Body>) -> Result<Self, RestError>
109 where
110 T: DeserializeOwned,
111 {
112 let status = resp.status();
113 tracing::trace!("http response status: {}", resp.status());
114 let bytes = hyper::body::to_bytes(resp.into_body())
115 .await
116 .map_err(RestError::from);
117 let value =
118 bytes.and_then(
119 |bytes| match serde_json::from_slice::<serde_json::Value>(&bytes) {
120 Ok(value) => {
121 tracing::debug!("resp: {value}");
122 Ok(Either::Left(value))
123 }
124 Err(_) => match std::str::from_utf8(&bytes) {
125 Ok(text) => Ok(Either::Right(text.to_string())),
126 Err(err) => Err(err.into()),
127 },
128 },
129 );
130 let res = match status {
131 StatusCode::TOO_MANY_REQUESTS => Err(RestError::Exchange(ExchangeError::RateLimited(
132 anyhow!("too many requests"),
133 ))),
134 StatusCode::IM_A_TEAPOT => Err(RestError::Exchange(ExchangeError::RateLimited(
135 anyhow!("I'm a teapot"),
136 ))),
137 StatusCode::SERVICE_UNAVAILABLE => Err(RestError::Exchange(match value {
138 Ok(msg) => ExchangeError::Unavailable(anyhow!("{msg}")),
139 Err(err) => ExchangeError::Unavailable(anyhow!("failed to read msg: {err}")),
140 })),
141 StatusCode::FORBIDDEN => match value {
142 Ok(Either::Left(value)) => Err(RestError::Exchange(ExchangeError::Forbidden(
143 anyhow!("{value}"),
144 ))),
145 Ok(Either::Right(_)) => Err(RestError::Exchange(ExchangeError::Forbidden(
146 anyhow!("no msg"),
147 ))),
148 Err(err) => Err(RestError::Exchange(ExchangeError::Forbidden(anyhow!(
149 "failed to parse msg: {err}"
150 )))),
151 },
152 _ => match value {
153 Ok(Either::Left(value)) => match serde_json::from_value::<T>(value) {
154 Ok(data) => Ok(Self { data }),
155 Err(err) => Err(err.into()),
156 },
157 Ok(Either::Right(text)) => Err(RestError::Text(text)),
158 Err(err) => Err(err),
159 },
160 };
161 tracing::trace!("finished processing http response");
162 res
163 }
164}