1use std::fmt;
5use std::str::FromStr;
6
7use serde::ser::{SerializeMap, SerializeSeq};
8use serde::{Deserialize, Serialize};
9
10pub(crate) fn deserialize_vec_or_empty_string<'de, D, T>(
17 deserializer: D,
18) -> Result<Vec<T>, D::Error>
19where
20 D: serde::Deserializer<'de>,
21 T: Deserialize<'de>,
22{
23 #[derive(Deserialize)]
24 #[serde(untagged)]
25 enum WireValue<T> {
26 Sequence(Vec<T>),
27 String(String),
28 Null(()),
29 }
30
31 match WireValue::<T>::deserialize(deserializer)? {
32 WireValue::Sequence(values) => Ok(values),
33 WireValue::String(value) if value.is_empty() => Ok(Vec::new()),
34 WireValue::String(value) => Err(serde::de::Error::custom(format!(
35 "expected an array or empty string, got {value:?}"
36 ))),
37 WireValue::Null(()) => Ok(Vec::new()),
38 }
39}
40
41#[derive(Debug, Clone, Deserialize)]
47pub(crate) struct OkxResponse<D> {
48 pub code: String,
49 pub msg: String,
50 pub data: D,
51}
52
53#[derive(Debug, Clone, Default, PartialEq, Eq, Hash, Serialize, Deserialize)]
70#[serde(transparent)]
71pub struct NumberString(String);
72
73impl NumberString {
74 pub fn as_str(&self) -> &str {
76 &self.0
77 }
78
79 pub fn is_empty(&self) -> bool {
82 self.0.is_empty()
83 }
84
85 pub fn parse<T: FromStr>(&self) -> Result<T, T::Err> {
88 self.0.parse()
89 }
90
91 pub fn into_string(self) -> String {
93 self.0
94 }
95
96 #[cfg(feature = "rust-decimal")]
98 pub fn to_decimal(&self) -> Result<rust_decimal::Decimal, rust_decimal::Error> {
99 self.0.parse()
100 }
101}
102
103impl From<String> for NumberString {
104 fn from(s: String) -> Self {
105 NumberString(s)
106 }
107}
108
109impl From<&str> for NumberString {
110 fn from(s: &str) -> Self {
111 NumberString(s.to_owned())
112 }
113}
114
115impl AsRef<str> for NumberString {
116 fn as_ref(&self) -> &str {
117 &self.0
118 }
119}
120
121impl fmt::Display for NumberString {
122 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
123 f.write_str(&self.0)
124 }
125}
126
127#[derive(Debug, Clone, Default)]
134pub struct RawRequestParams {
135 fields: Vec<(String, ParamValue)>,
136}
137
138impl RawRequestParams {
139 pub fn new() -> Self {
141 Self::default()
142 }
143
144 pub fn param(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
146 self.set(key.into(), ParamValue::String(value.into()));
147 self
148 }
149
150 pub fn bool_param(mut self, key: impl Into<String>, value: bool) -> Self {
152 self.set(key.into(), ParamValue::Bool(value));
153 self
154 }
155
156 pub fn string_list<I, S>(mut self, key: impl Into<String>, values: I) -> Self
158 where
159 I: IntoIterator<Item = S>,
160 S: Into<String>,
161 {
162 self.set(
163 key.into(),
164 ParamValue::StringList(values.into_iter().map(Into::into).collect()),
165 );
166 self
167 }
168
169 pub fn is_empty(&self) -> bool {
171 self.fields.is_empty()
172 }
173
174 fn set(&mut self, key: String, value: ParamValue) {
175 if let Some((_, existing)) = self.fields.iter_mut().find(|(name, _)| name == &key) {
176 *existing = value;
177 } else {
178 self.fields.push((key, value));
179 }
180 }
181}
182
183impl Serialize for RawRequestParams {
184 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
185 let mut map = serializer.serialize_map(Some(self.fields.len()))?;
186 for (key, value) in &self.fields {
187 map.serialize_entry(key, value)?;
188 }
189 map.end()
190 }
191}
192
193pub type RequestParams = RawRequestParams;
198
199#[derive(Debug, Clone)]
200enum ParamValue {
201 String(String),
202 Bool(bool),
203 StringList(Vec<String>),
204}
205
206impl Serialize for ParamValue {
207 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
208 match self {
209 Self::String(value) => serializer.serialize_str(value),
210 Self::Bool(value) => serializer.serialize_bool(*value),
211 Self::StringList(values) => {
212 let mut seq = serializer.serialize_seq(Some(values.len()))?;
213 for value in values {
214 seq.serialize_element(value)?;
215 }
216 seq.end()
217 }
218 }
219 }
220}
221
222#[derive(Debug, Clone, Default, Deserialize)]
227#[serde(rename_all = "camelCase")]
228#[non_exhaustive]
229pub struct RestRow {
230 #[serde(default, rename = "instType")]
232 pub inst_type: String,
233 #[serde(default, rename = "instId")]
235 pub inst_id: String,
236 #[serde(default, rename = "instFamily")]
238 pub inst_family: String,
239 #[serde(default)]
241 pub ccy: String,
242 #[serde(default, rename = "ordId")]
244 pub ord_id: String,
245 #[serde(default, rename = "clOrdId")]
247 pub cl_ord_id: String,
248 #[serde(default, rename = "algoId")]
250 pub algo_id: String,
251 #[serde(default, rename = "algoClOrdId")]
253 pub algo_cl_ord_id: String,
254 #[serde(default, rename = "quoteId")]
256 pub quote_id: String,
257 #[serde(default, rename = "reqId")]
259 pub req_id: String,
260 #[serde(default, rename = "productId")]
262 pub product_id: String,
263 #[serde(default, rename = "type")]
265 pub row_type: String,
266 #[serde(default)]
268 pub state: String,
269 #[serde(default)]
271 pub status: String,
272 #[serde(default)]
274 pub side: String,
275 #[serde(default)]
277 pub amt: NumberString,
278 #[serde(default)]
280 pub sz: NumberString,
281 #[serde(default)]
283 pub px: NumberString,
284 #[serde(default)]
286 pub rate: NumberString,
287 #[serde(default)]
289 pub bal: NumberString,
290 #[serde(default)]
292 pub avail_bal: NumberString,
293 #[serde(default)]
295 pub ts: NumberString,
296 #[serde(default, rename = "sCode")]
298 pub s_code: String,
299 #[serde(default, rename = "sMsg")]
301 pub s_msg: String,
302}
303
304macro_rules! string_enum {
308 (
309 $(#[$meta:meta])*
310 $vis:vis enum $name:ident {
311 $( $(#[$vmeta:meta])* $variant:ident = $wire:literal ),* $(,)?
312 }
313 ) => {
314 $(#[$meta])*
315 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
316 #[non_exhaustive]
317 $vis enum $name {
318 $( $(#[$vmeta])* $variant, )*
319 Unknown(String),
322 }
323
324 impl $name {
325 pub fn as_str(&self) -> &str {
327 match self {
328 $( $name::$variant => $wire, )*
329 $name::Unknown(s) => s.as_str(),
330 }
331 }
332 }
333
334 impl ::core::convert::From<&str> for $name {
335 fn from(s: &str) -> Self {
336 match s {
337 $( $wire => $name::$variant, )*
338 other => $name::Unknown(other.to_owned()),
339 }
340 }
341 }
342
343 impl ::core::fmt::Display for $name {
344 fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
345 f.write_str(self.as_str())
346 }
347 }
348
349 impl ::serde::Serialize for $name {
350 fn serialize<S: ::serde::Serializer>(&self, ser: S) -> ::core::result::Result<S::Ok, S::Error> {
351 ser.serialize_str(self.as_str())
352 }
353 }
354
355 impl<'de> ::serde::Deserialize<'de> for $name {
356 fn deserialize<D: ::serde::Deserializer<'de>>(de: D) -> ::core::result::Result<Self, D::Error> {
357 let s = <::std::string::String as ::serde::Deserialize>::deserialize(de)?;
358 ::core::result::Result::Ok($name::from(s.as_str()))
359 }
360 }
361 };
362}
363
364string_enum! {
365 pub enum InstType {
367 Spot = "SPOT",
369 Margin = "MARGIN",
371 Swap = "SWAP",
373 Futures = "FUTURES",
375 Option = "OPTION",
377 Events = "EVENTS",
379 }
380}
381
382string_enum! {
383 pub enum OrderSide {
385 Buy = "buy",
387 Sell = "sell",
389 }
390}
391
392string_enum! {
393 pub enum OrderType {
395 Market = "market",
397 Limit = "limit",
399 PostOnly = "post_only",
401 Fok = "fok",
403 Ioc = "ioc",
405 OptimalLimitIoc = "optimal_limit_ioc",
407 }
408}
409
410string_enum! {
411 pub enum TradeMode {
413 Cash = "cash",
415 Cross = "cross",
417 Isolated = "isolated",
419 SpotIsolated = "spot_isolated",
421 }
422}
423
424string_enum! {
425 pub enum PositionSide {
427 Long = "long",
429 Short = "short",
431 Net = "net",
433 }
434}
435
436string_enum! {
437 pub enum OrderState {
439 Live = "live",
441 PartiallyFilled = "partially_filled",
443 Filled = "filled",
445 Canceled = "canceled",
447 MmpCanceled = "mmp_canceled",
449 }
450}
451
452#[cfg(test)]
453mod tests {
454 use super::*;
455
456 #[test]
457 fn number_string_parses_and_preserves() {
458 let n = NumberString::from("1.005");
459 assert_eq!(n.as_str(), "1.005");
460 assert_eq!(n.parse::<f64>().unwrap(), 1.005);
461 assert_eq!(n.into_string(), "1.005");
462 }
463
464 #[test]
465 fn known_enum_value_round_trips() {
466 let v: InstType = serde_json::from_str("\"SWAP\"").unwrap();
467 assert_eq!(v, InstType::Swap);
468 assert_eq!(serde_json::to_string(&v).unwrap(), "\"SWAP\"");
469 }
470
471 #[test]
472 fn unknown_enum_value_is_preserved_not_an_error() {
473 let v: InstType = serde_json::from_str("\"FUTURE_THING\"").unwrap();
474 assert_eq!(v, InstType::Unknown("FUTURE_THING".to_owned()));
475 assert_eq!(serde_json::to_string(&v).unwrap(), "\"FUTURE_THING\"");
477 }
478
479 #[test]
480 fn raw_request_params_replace_duplicate_keys() {
481 let params = RawRequestParams::new()
482 .param("ccy", "BTC")
483 .param("ccy", "ETH");
484
485 assert_eq!(
486 serde_json::to_value(params).unwrap(),
487 serde_json::json!({"ccy": "ETH"})
488 );
489 }
490}