xapi-binance 0.0.1

Binance API client
Documentation
use http::StatusCode;
use serde::{Deserialize, Serialize};
use std::fmt::{Display, Formatter};
use xapi_shared::{
    repr::de_status_code::deserialize_status_code,
    rest::error::SharedRestError,
    ws::{error::SharedWsError, response::SharedWsResponseTrait},
};

pub type BnRestRespType<T> = Result<T, SharedRestError<BnRespError>>;
pub type BnWsApiRespType<T> = Result<T, SharedWsError>;

#[derive(Debug, Serialize, Deserialize)]
pub struct BnRespError {
    pub code: i32,
    pub msg: String,
}

impl Display for BnRespError {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        write!(
            f,
            "{}",
            serde_json::to_string(self).unwrap_or_else(|_| String::from("{}"))
        )
    }
}

#[derive(Debug)]
pub struct BnWsApiResponse {
    pub id: String,
    pub result: serde_json::Value,
}

impl SharedWsResponseTrait<String> for BnWsApiResponse {
    fn try_parse(text: &str) -> Option<Result<Self, (String, SharedWsError)>>
    where
        Self: Sized,
    {
        match serde_json::from_str::<BnWsApiRawResponse>(text) {
            Err(_) => None,
            Ok(resp) => {
                if !resp.status.is_success() || resp.error.is_some() || resp.result.is_none() {
                    tracing::error!(
                        status = ?resp.status,
                        id = resp.id,
                        ?resp,
                        "ws api request failed with status code"
                    );
                    let err_str = serde_json::to_string(&resp.error)
                        .inspect_err(|err| {
                            tracing::error!(?err, "failed to serialize ws api error response")
                        })
                        .unwrap_or_else(|_| format!("{:?}", resp.error));
                    return Some(Err((resp.id, SharedWsError::AppError(err_str))));
                }

                Some(Ok(Self {
                    id: resp.id,
                    result: resp.result.unwrap(),
                }))
            }
        }
    }

    fn get_id(&self) -> &String {
        &self.id
    }
}

#[derive(Debug, Deserialize)]
struct BnWsApiRawResponse {
    id: String,
    #[serde(deserialize_with = "deserialize_status_code")]
    status: StatusCode,
    result: Option<serde_json::Value>,
    error: Option<BnRespError>,
}

#[derive(Debug)]
pub struct BnWsStreamResponse {
    pub id: String,
    pub result: serde_json::Value,
}

impl SharedWsResponseTrait<String> for BnWsStreamResponse {
    fn try_parse(text: &str) -> Option<Result<Self, (String, SharedWsError)>>
    where
        Self: Sized,
    {
        match serde_json::from_str::<BnWsStreamRawResponse>(text) {
            Err(_) => None,
            Ok(resp) => {
                if let Some(err) = &resp.error {
                    tracing::error!(id = resp.id, ?err, "ws stream request failed with error");
                    return Some(Err((resp.id, SharedWsError::AppError(err.msg.clone()))));
                }

                Some(Ok(Self {
                    id: resp.id,
                    result: resp.result.unwrap_or_default(),
                }))
            }
        }
    }

    fn get_id(&self) -> &String {
        &self.id
    }
}

#[derive(Debug, Deserialize)]
struct BnWsStreamRawResponse {
    id: String,
    result: Option<serde_json::Value>,
    error: Option<BnWsStreamError>,
}

#[derive(Debug, Deserialize)]
struct BnWsStreamError {
    #[allow(dead_code)]
    code: i64,
    msg: String,
    #[allow(dead_code)]
    id: Option<String>,
}

#[derive(Debug, Deserialize)]
pub struct BnWsStreamData {
    pub stream: String,
    pub data: serde_json::Value,
}

impl SharedWsResponseTrait<String> for BnWsStreamData {
    fn try_parse(text: &str) -> Option<Result<Self, (String, SharedWsError)>>
    where
        Self: Sized,
    {
        match serde_json::from_str(text) {
            Err(_) => None,
            Ok(data) => Some(Ok(data)),
        }
    }

    fn get_id(&self) -> &String {
        &self.stream
    }
}