use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize)]
#[serde(rename_all = "lowercase")]
pub enum WsOp {
Subscribe,
Unsubscribe,
Auth,
Ping,
}
#[derive(Debug, Clone, Serialize)]
pub struct WsRequest {
pub op: WsOp,
pub args: Vec<serde_json::Value>,
}
impl WsRequest {
pub fn subscribe(topics: Vec<String>) -> Self {
WsRequest {
op: WsOp::Subscribe,
args: topics.into_iter().map(serde_json::Value::String).collect(),
}
}
pub fn unsubscribe(topics: Vec<String>) -> Self {
WsRequest {
op: WsOp::Unsubscribe,
args: topics.into_iter().map(serde_json::Value::String).collect(),
}
}
pub fn auth(api_key: &str, expires: u64, signature: &str) -> Self {
WsRequest {
op: WsOp::Auth,
args: vec![
serde_json::Value::String(api_key.to_string()),
serde_json::Value::Number(serde_json::Number::from(expires)),
serde_json::Value::String(signature.to_string()),
],
}
}
pub fn ping() -> Self {
WsRequest {
op: WsOp::Ping,
args: vec![],
}
}
}
#[derive(Debug, Clone, Deserialize)]
pub struct WsResponse {
#[serde(default)]
pub topic: Option<String>,
#[serde(rename = "type")]
#[serde(default)]
pub msg_type: Option<String>,
#[serde(default)]
pub ts: Option<i64>,
#[serde(default)]
pub data: Option<serde_json::Value>,
}
#[derive(Debug, Clone, Deserialize)]
pub struct WsOpResponse {
#[serde(default)]
pub op: Option<String>,
#[serde(default)]
pub success: Option<bool>,
#[serde(default)]
pub ret_msg: Option<String>,
#[serde(default)]
pub conn_id: Option<String>,
#[serde(default)]
pub req_id: Option<String>,
}
#[derive(Debug, Clone, Deserialize)]
#[serde(untagged)]
pub enum WsMessage {
Data(WsResponse),
Op(WsOpResponse),
}
impl WsMessage {
pub fn is_subscribe_success(&self) -> bool {
matches!(self, WsMessage::Op(WsOpResponse {
op: Some(op),
success: Some(true),
..
}) if op == "subscribe")
}
pub fn is_auth_success(&self) -> bool {
matches!(self, WsMessage::Op(WsOpResponse {
op: Some(op),
success: Some(true),
..
}) if op == "auth")
}
pub fn is_pong(&self) -> bool {
matches!(self, WsMessage::Op(WsOpResponse {
op: Some(op),
..
}) if op == "pong")
}
pub fn topic(&self) -> Option<&str> {
match self {
WsMessage::Data(r) => r.topic.as_deref(),
_ => None,
}
}
pub fn msg_type(&self) -> Option<&str> {
match self {
WsMessage::Data(r) => r.msg_type.as_deref(),
_ => None,
}
}
}
pub mod topics {
pub fn orderbook(depth: u16, symbol: &str) -> String {
format!("orderbook.{}.{}", depth, symbol)
}
pub fn trade(symbol: &str) -> String {
format!("publicTrade.{}", symbol)
}
pub mod ticker {
pub fn linear(symbol: &str) -> String {
format!("tickers.{}", symbol)
}
pub fn inverse(symbol: &str) -> String {
format!("tickers.{}", symbol)
}
pub fn spot(symbol: &str) -> String {
format!("tickers.{}", symbol)
}
pub fn option(symbol: &str) -> String {
format!("tickers.{}", symbol)
}
}
pub fn kline(interval: &str, symbol: &str) -> String {
format!("kline.{}.{}", symbol, interval)
}
pub fn liquidation(symbol: &str) -> String {
format!("liquidation.{}", symbol)
}
pub mod position {
pub fn all() -> String {
"position".to_string()
}
pub fn linear() -> String {
"position.linear".to_string()
}
pub fn inverse() -> String {
"position.inverse".to_string()
}
pub fn option() -> String {
"position.option".to_string()
}
}
pub mod execution {
pub fn all() -> String {
"execution".to_string()
}
pub fn linear() -> String {
"execution.linear".to_string()
}
}
pub mod order {
pub fn all() -> String {
"order".to_string()
}
pub fn linear() -> String {
"order.linear".to_string()
}
}
pub mod wallet {
pub fn all() -> String {
"wallet".to_string()
}
pub fn linear() -> String {
"wallet.linear".to_string()
}
}
}