use super::error::RestError;
use anyhow::anyhow;
use either::Either;
use exc_core::ExchangeError;
use http::StatusCode;
use serde::{de::DeserializeOwned, Deserialize};
pub mod instrument;
pub mod candle;
pub mod listen_key;
pub mod error_message;
pub mod trading;
pub mod account;
pub use self::{
account::{
SubAccountBalances, SubAccountFutures, SubAccountFuturesPositions, SubAccountMargin,
SubAccounts,
},
candle::Candle,
error_message::ErrorMessage,
instrument::{ExchangeInfo, SpotExchangeInfo, UFExchangeInfo},
listen_key::ListenKey,
trading::Order,
};
pub type Candles = Vec<Candle>;
pub type Unknown = serde_json::Value;
#[allow(clippy::large_enum_variant)]
#[derive(Debug, Deserialize)]
#[serde(untagged)]
pub enum Data {
Candles(Vec<Candle>),
ExchangeInfo(ExchangeInfo),
ListenKey(ListenKey),
Error(ErrorMessage),
Order(Order),
SubAccounts(SubAccounts),
SubAccountBalances(SubAccountBalances),
SubAccountMargin(SubAccountMargin),
SubAccountFutures(SubAccountFutures),
SubAccountFuturesPositions(SubAccountFuturesPositions),
Unknwon(Unknown),
}
impl TryFrom<Data> for Unknown {
type Error = RestError;
fn try_from(value: Data) -> Result<Self, Self::Error> {
match value {
Data::Unknwon(u) => Ok(u),
_ => Err(RestError::UnexpectedResponseType(anyhow::anyhow!(
"{value:?}"
))),
}
}
}
#[derive(Debug)]
pub struct RestResponse<T> {
data: T,
}
impl<T> RestResponse<T> {
pub fn into_inner(self) -> T {
self.data
}
pub fn into_response<U>(self) -> Result<U, RestError>
where
U: TryFrom<T, Error = RestError>,
{
U::try_from(self.into_inner())
}
pub(crate) async fn from_http(resp: http::Response<hyper::Body>) -> Result<Self, RestError>
where
T: DeserializeOwned,
{
let status = resp.status();
tracing::trace!("http response status: {}", resp.status());
let bytes = hyper::body::to_bytes(resp.into_body())
.await
.map_err(RestError::from);
let value =
bytes.and_then(
|bytes| match serde_json::from_slice::<serde_json::Value>(&bytes) {
Ok(value) => {
tracing::debug!("resp: {value}");
Ok(Either::Left(value))
}
Err(_) => match std::str::from_utf8(&bytes) {
Ok(text) => Ok(Either::Right(text.to_string())),
Err(err) => Err(err.into()),
},
},
);
let res = match status {
StatusCode::TOO_MANY_REQUESTS => Err(RestError::Exchange(ExchangeError::RateLimited(
anyhow!("too many requests"),
))),
StatusCode::IM_A_TEAPOT => Err(RestError::Exchange(ExchangeError::RateLimited(
anyhow!("I'm a teapot"),
))),
StatusCode::SERVICE_UNAVAILABLE => Err(RestError::Exchange(match value {
Ok(msg) => ExchangeError::Unavailable(anyhow!("{msg}")),
Err(err) => ExchangeError::Unavailable(anyhow!("failed to read msg: {err}")),
})),
StatusCode::FORBIDDEN => match value {
Ok(Either::Left(value)) => Err(RestError::Exchange(ExchangeError::Forbidden(
anyhow!("{value}"),
))),
Ok(Either::Right(_)) => Err(RestError::Exchange(ExchangeError::Forbidden(
anyhow!("no msg"),
))),
Err(err) => Err(RestError::Exchange(ExchangeError::Forbidden(anyhow!(
"failed to parse msg: {err}"
)))),
},
_ => match value {
Ok(Either::Left(value)) => match serde_json::from_value::<T>(value) {
Ok(data) => Ok(Self { data }),
Err(err) => Err(err.into()),
},
Ok(Either::Right(text)) => Err(RestError::Text(text)),
Err(err) => Err(err),
},
};
tracing::trace!("finished processing http response");
res
}
}