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
41mod validation;
42
43pub use validation::{RequestValidationError, ValidateRequest};
44pub(crate) use validation::{
45 at_least_one, collection_length, decimal_string_range, exactly_one, length_range, max_length,
46 non_empty, non_empty_items, non_negative_decimal_string, one_of, optional_non_empty,
47 optional_one_of, optional_positive_decimal_string, optional_unsigned_integer_string,
48 positive_decimal_string, positive_unsigned_integer_string, range_u64, reject_when_present,
49 require_when, validate_client_request_id, validate_side,
50};
51
52#[derive(Debug, Clone, Deserialize)]
58pub(crate) struct OkxResponse<D> {
59 pub code: String,
60 pub msg: String,
61 pub data: D,
62}
63
64#[derive(Debug, Clone, Default, PartialEq, Eq, Hash, Serialize, Deserialize)]
81#[serde(transparent)]
82pub struct NumberString(String);
83
84impl NumberString {
85 pub fn as_str(&self) -> &str {
87 &self.0
88 }
89
90 pub fn is_empty(&self) -> bool {
93 self.0.is_empty()
94 }
95
96 pub fn parse<T: FromStr>(&self) -> Result<T, T::Err> {
99 self.0.parse()
100 }
101
102 pub fn into_string(self) -> String {
104 self.0
105 }
106
107 #[cfg(feature = "rust-decimal")]
109 pub fn to_decimal(&self) -> Result<rust_decimal::Decimal, rust_decimal::Error> {
110 self.0.parse()
111 }
112}
113
114impl From<String> for NumberString {
115 fn from(s: String) -> Self {
116 NumberString(s)
117 }
118}
119
120impl From<&str> for NumberString {
121 fn from(s: &str) -> Self {
122 NumberString(s.to_owned())
123 }
124}
125
126impl AsRef<str> for NumberString {
127 fn as_ref(&self) -> &str {
128 &self.0
129 }
130}
131
132impl fmt::Display for NumberString {
133 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
134 f.write_str(&self.0)
135 }
136}
137
138#[derive(Debug, Clone, Default)]
145pub struct RawRequestParams {
146 fields: Vec<(String, ParamValue)>,
147}
148
149impl RawRequestParams {
150 pub fn new() -> Self {
152 Self::default()
153 }
154
155 pub fn param(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
157 self.set(key.into(), ParamValue::String(value.into()));
158 self
159 }
160
161 pub fn bool_param(mut self, key: impl Into<String>, value: bool) -> Self {
163 self.set(key.into(), ParamValue::Bool(value));
164 self
165 }
166
167 pub fn string_list<I, S>(mut self, key: impl Into<String>, values: I) -> Self
169 where
170 I: IntoIterator<Item = S>,
171 S: Into<String>,
172 {
173 self.set(
174 key.into(),
175 ParamValue::StringList(values.into_iter().map(Into::into).collect()),
176 );
177 self
178 }
179
180 pub fn is_empty(&self) -> bool {
182 self.fields.is_empty()
183 }
184
185 fn set(&mut self, key: String, value: ParamValue) {
186 if let Some((_, existing)) = self.fields.iter_mut().find(|(name, _)| name == &key) {
187 *existing = value;
188 } else {
189 self.fields.push((key, value));
190 }
191 }
192}
193
194impl Serialize for RawRequestParams {
195 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
196 let mut map = serializer.serialize_map(Some(self.fields.len()))?;
197 for (key, value) in &self.fields {
198 map.serialize_entry(key, value)?;
199 }
200 map.end()
201 }
202}
203
204pub type RequestParams = RawRequestParams;
209
210#[derive(Debug, Clone)]
211enum ParamValue {
212 String(String),
213 Bool(bool),
214 StringList(Vec<String>),
215}
216
217impl Serialize for ParamValue {
218 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
219 match self {
220 Self::String(value) => serializer.serialize_str(value),
221 Self::Bool(value) => serializer.serialize_bool(*value),
222 Self::StringList(values) => {
223 let mut seq = serializer.serialize_seq(Some(values.len()))?;
224 for value in values {
225 seq.serialize_element(value)?;
226 }
227 seq.end()
228 }
229 }
230 }
231}
232
233#[derive(Debug, Clone, Default, Deserialize)]
238#[serde(rename_all = "camelCase")]
239#[non_exhaustive]
240pub struct RestRow {
241 #[serde(default, rename = "instType")]
243 pub inst_type: String,
244 #[serde(default, rename = "instId")]
246 pub inst_id: String,
247 #[serde(default, rename = "instFamily")]
249 pub inst_family: String,
250 #[serde(default)]
252 pub ccy: String,
253 #[serde(default, rename = "ordId")]
255 pub ord_id: String,
256 #[serde(default, rename = "clOrdId")]
258 pub cl_ord_id: String,
259 #[serde(default, rename = "algoId")]
261 pub algo_id: String,
262 #[serde(default, rename = "algoClOrdId")]
264 pub algo_cl_ord_id: String,
265 #[serde(default, rename = "quoteId")]
267 pub quote_id: String,
268 #[serde(default, rename = "reqId")]
270 pub req_id: String,
271 #[serde(default, rename = "productId")]
273 pub product_id: String,
274 #[serde(default, rename = "type")]
276 pub row_type: String,
277 #[serde(default)]
279 pub state: String,
280 #[serde(default)]
282 pub status: String,
283 #[serde(default)]
285 pub side: String,
286 #[serde(default)]
288 pub amt: NumberString,
289 #[serde(default)]
291 pub sz: NumberString,
292 #[serde(default)]
294 pub px: NumberString,
295 #[serde(default)]
297 pub rate: NumberString,
298 #[serde(default)]
300 pub bal: NumberString,
301 #[serde(default)]
303 pub avail_bal: NumberString,
304 #[serde(default)]
306 pub ts: NumberString,
307 #[serde(default, rename = "sCode")]
309 pub s_code: String,
310 #[serde(default, rename = "sMsg")]
312 pub s_msg: String,
313}
314
315macro_rules! string_enum {
319 (
320 $(#[$meta:meta])*
321 $vis:vis enum $name:ident {
322 $( $(#[$vmeta:meta])* $variant:ident = $wire:literal ),* $(,)?
323 }
324 ) => {
325 $(#[$meta])*
326 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
327 #[non_exhaustive]
328 $vis enum $name {
329 $( $(#[$vmeta])* $variant, )*
330 Unknown(String),
333 }
334
335 impl $name {
336 pub fn as_str(&self) -> &str {
338 match self {
339 $( $name::$variant => $wire, )*
340 $name::Unknown(s) => s.as_str(),
341 }
342 }
343 }
344
345 impl ::core::convert::From<&str> for $name {
346 fn from(s: &str) -> Self {
347 match s {
348 $( $wire => $name::$variant, )*
349 other => $name::Unknown(other.to_owned()),
350 }
351 }
352 }
353
354 impl ::core::fmt::Display for $name {
355 fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
356 f.write_str(self.as_str())
357 }
358 }
359
360 impl ::serde::Serialize for $name {
361 fn serialize<S: ::serde::Serializer>(&self, ser: S) -> ::core::result::Result<S::Ok, S::Error> {
362 ser.serialize_str(self.as_str())
363 }
364 }
365
366 impl<'de> ::serde::Deserialize<'de> for $name {
367 fn deserialize<D: ::serde::Deserializer<'de>>(de: D) -> ::core::result::Result<Self, D::Error> {
368 let s = <::std::string::String as ::serde::Deserialize>::deserialize(de)?;
369 ::core::result::Result::Ok($name::from(s.as_str()))
370 }
371 }
372 };
373}
374
375string_enum! {
376 pub enum InstType {
378 Spot = "SPOT",
380 Margin = "MARGIN",
382 Swap = "SWAP",
384 Futures = "FUTURES",
386 Option = "OPTION",
388 Events = "EVENTS",
390 }
391}
392
393string_enum! {
394 pub enum OrderSide {
396 Buy = "buy",
398 Sell = "sell",
400 }
401}
402
403string_enum! {
404 pub enum OrderType {
406 Market = "market",
408 Limit = "limit",
410 PostOnly = "post_only",
412 Fok = "fok",
414 Ioc = "ioc",
416 OptimalLimitIoc = "optimal_limit_ioc",
418 }
419}
420
421string_enum! {
422 pub enum TradeMode {
424 Cash = "cash",
426 Cross = "cross",
428 Isolated = "isolated",
430 SpotIsolated = "spot_isolated",
432 }
433}
434
435string_enum! {
436 pub enum PositionSide {
438 Long = "long",
440 Short = "short",
442 Net = "net",
444 }
445}
446
447string_enum! {
448 pub enum OrderState {
450 Live = "live",
452 PartiallyFilled = "partially_filled",
454 Filled = "filled",
456 Canceled = "canceled",
458 MmpCanceled = "mmp_canceled",
460 }
461}
462
463#[cfg(test)]
464mod tests {
465 use super::*;
466
467 #[test]
468 fn number_string_parses_and_preserves() {
469 let n = NumberString::from("1.005");
470 assert_eq!(n.as_str(), "1.005");
471 assert_eq!(n.parse::<f64>().unwrap(), 1.005);
472 assert_eq!(n.into_string(), "1.005");
473 }
474
475 #[test]
476 fn known_enum_value_round_trips() {
477 let v: InstType = serde_json::from_str("\"SWAP\"").unwrap();
478 assert_eq!(v, InstType::Swap);
479 assert_eq!(serde_json::to_string(&v).unwrap(), "\"SWAP\"");
480 }
481
482 #[test]
483 fn unknown_enum_value_is_preserved_not_an_error() {
484 let v: InstType = serde_json::from_str("\"FUTURE_THING\"").unwrap();
485 assert_eq!(v, InstType::Unknown("FUTURE_THING".to_owned()));
486 assert_eq!(serde_json::to_string(&v).unwrap(), "\"FUTURE_THING\"");
488 }
489
490 #[test]
491 fn raw_request_params_replace_duplicate_keys() {
492 let params = RawRequestParams::new()
493 .param("ccy", "BTC")
494 .param("ccy", "ETH");
495
496 assert_eq!(
497 serde_json::to_value(params).unwrap(),
498 serde_json::json!({"ccy": "ETH"})
499 );
500 }
501}