1use std::fmt;
5use std::str::FromStr;
6
7use serde::ser::{SerializeMap, SerializeSeq};
8use serde::{Deserialize, Serialize};
9
10#[derive(Debug, Clone, Deserialize)]
16pub(crate) struct OkxResponse<D> {
17 pub code: String,
18 pub msg: String,
19 pub data: D,
20}
21
22#[derive(Debug, Clone, Default, PartialEq, Eq, Hash, Serialize, Deserialize)]
39#[serde(transparent)]
40pub struct NumberString(String);
41
42impl NumberString {
43 pub fn as_str(&self) -> &str {
45 &self.0
46 }
47
48 pub fn is_empty(&self) -> bool {
51 self.0.is_empty()
52 }
53
54 pub fn parse<T: FromStr>(&self) -> Result<T, T::Err> {
57 self.0.parse()
58 }
59
60 pub fn into_string(self) -> String {
62 self.0
63 }
64
65 #[cfg(feature = "rust-decimal")]
67 pub fn to_decimal(&self) -> Result<rust_decimal::Decimal, rust_decimal::Error> {
68 self.0.parse()
69 }
70}
71
72impl From<String> for NumberString {
73 fn from(s: String) -> Self {
74 NumberString(s)
75 }
76}
77
78impl From<&str> for NumberString {
79 fn from(s: &str) -> Self {
80 NumberString(s.to_owned())
81 }
82}
83
84impl AsRef<str> for NumberString {
85 fn as_ref(&self) -> &str {
86 &self.0
87 }
88}
89
90impl fmt::Display for NumberString {
91 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
92 f.write_str(&self.0)
93 }
94}
95
96#[derive(Debug, Clone, Default)]
102pub struct RequestParams {
103 fields: Vec<(String, ParamValue)>,
104}
105
106impl RequestParams {
107 pub fn new() -> Self {
109 Self::default()
110 }
111
112 pub fn param(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
114 self.fields
115 .push((key.into(), ParamValue::String(value.into())));
116 self
117 }
118
119 pub fn bool_param(mut self, key: impl Into<String>, value: bool) -> Self {
121 self.fields.push((key.into(), ParamValue::Bool(value)));
122 self
123 }
124
125 pub fn string_list<I, S>(mut self, key: impl Into<String>, values: I) -> Self
127 where
128 I: IntoIterator<Item = S>,
129 S: Into<String>,
130 {
131 self.fields.push((
132 key.into(),
133 ParamValue::StringList(values.into_iter().map(Into::into).collect()),
134 ));
135 self
136 }
137
138 pub fn is_empty(&self) -> bool {
140 self.fields.is_empty()
141 }
142}
143
144impl Serialize for RequestParams {
145 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
146 let mut map = serializer.serialize_map(Some(self.fields.len()))?;
147 for (key, value) in &self.fields {
148 map.serialize_entry(key, value)?;
149 }
150 map.end()
151 }
152}
153
154#[derive(Debug, Clone)]
155enum ParamValue {
156 String(String),
157 Bool(bool),
158 StringList(Vec<String>),
159}
160
161impl Serialize for ParamValue {
162 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
163 match self {
164 Self::String(value) => serializer.serialize_str(value),
165 Self::Bool(value) => serializer.serialize_bool(*value),
166 Self::StringList(values) => {
167 let mut seq = serializer.serialize_seq(Some(values.len()))?;
168 for value in values {
169 seq.serialize_element(value)?;
170 }
171 seq.end()
172 }
173 }
174 }
175}
176
177#[derive(Debug, Clone, Default, Deserialize)]
182#[serde(rename_all = "camelCase")]
183#[non_exhaustive]
184pub struct RestRow {
185 #[serde(default, rename = "instType")]
187 pub inst_type: String,
188 #[serde(default, rename = "instId")]
190 pub inst_id: String,
191 #[serde(default, rename = "instFamily")]
193 pub inst_family: String,
194 #[serde(default)]
196 pub ccy: String,
197 #[serde(default, rename = "ordId")]
199 pub ord_id: String,
200 #[serde(default, rename = "clOrdId")]
202 pub cl_ord_id: String,
203 #[serde(default, rename = "algoId")]
205 pub algo_id: String,
206 #[serde(default, rename = "algoClOrdId")]
208 pub algo_cl_ord_id: String,
209 #[serde(default, rename = "quoteId")]
211 pub quote_id: String,
212 #[serde(default, rename = "reqId")]
214 pub req_id: String,
215 #[serde(default, rename = "productId")]
217 pub product_id: String,
218 #[serde(default, rename = "type")]
220 pub row_type: String,
221 #[serde(default)]
223 pub state: String,
224 #[serde(default)]
226 pub status: String,
227 #[serde(default)]
229 pub side: String,
230 #[serde(default)]
232 pub amt: NumberString,
233 #[serde(default)]
235 pub sz: NumberString,
236 #[serde(default)]
238 pub px: NumberString,
239 #[serde(default)]
241 pub rate: NumberString,
242 #[serde(default)]
244 pub bal: NumberString,
245 #[serde(default)]
247 pub avail_bal: NumberString,
248 #[serde(default)]
250 pub ts: NumberString,
251 #[serde(default, rename = "sCode")]
253 pub s_code: String,
254 #[serde(default, rename = "sMsg")]
256 pub s_msg: String,
257}
258
259macro_rules! string_enum {
263 (
264 $(#[$meta:meta])*
265 $vis:vis enum $name:ident {
266 $( $(#[$vmeta:meta])* $variant:ident = $wire:literal ),* $(,)?
267 }
268 ) => {
269 $(#[$meta])*
270 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
271 #[non_exhaustive]
272 $vis enum $name {
273 $( $(#[$vmeta])* $variant, )*
274 Unknown(String),
277 }
278
279 impl $name {
280 pub fn as_str(&self) -> &str {
282 match self {
283 $( $name::$variant => $wire, )*
284 $name::Unknown(s) => s.as_str(),
285 }
286 }
287 }
288
289 impl ::core::convert::From<&str> for $name {
290 fn from(s: &str) -> Self {
291 match s {
292 $( $wire => $name::$variant, )*
293 other => $name::Unknown(other.to_owned()),
294 }
295 }
296 }
297
298 impl ::core::fmt::Display for $name {
299 fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
300 f.write_str(self.as_str())
301 }
302 }
303
304 impl ::serde::Serialize for $name {
305 fn serialize<S: ::serde::Serializer>(&self, ser: S) -> ::core::result::Result<S::Ok, S::Error> {
306 ser.serialize_str(self.as_str())
307 }
308 }
309
310 impl<'de> ::serde::Deserialize<'de> for $name {
311 fn deserialize<D: ::serde::Deserializer<'de>>(de: D) -> ::core::result::Result<Self, D::Error> {
312 let s = <::std::string::String as ::serde::Deserialize>::deserialize(de)?;
313 ::core::result::Result::Ok($name::from(s.as_str()))
314 }
315 }
316 };
317}
318
319string_enum! {
320 pub enum InstType {
322 Spot = "SPOT",
324 Margin = "MARGIN",
326 Swap = "SWAP",
328 Futures = "FUTURES",
330 Option = "OPTION",
332 }
333}
334
335string_enum! {
336 pub enum OrderSide {
338 Buy = "buy",
340 Sell = "sell",
342 }
343}
344
345string_enum! {
346 pub enum OrderType {
348 Market = "market",
350 Limit = "limit",
352 PostOnly = "post_only",
354 Fok = "fok",
356 Ioc = "ioc",
358 OptimalLimitIoc = "optimal_limit_ioc",
360 }
361}
362
363string_enum! {
364 pub enum TradeMode {
366 Cash = "cash",
368 Cross = "cross",
370 Isolated = "isolated",
372 }
373}
374
375string_enum! {
376 pub enum PositionSide {
378 Long = "long",
380 Short = "short",
382 Net = "net",
384 }
385}
386
387string_enum! {
388 pub enum OrderState {
390 Live = "live",
392 PartiallyFilled = "partially_filled",
394 Filled = "filled",
396 Canceled = "canceled",
398 MmpCanceled = "mmp_canceled",
400 }
401}
402
403#[cfg(test)]
404mod tests {
405 use super::*;
406
407 #[test]
408 fn number_string_parses_and_preserves() {
409 let n = NumberString::from("1.005");
410 assert_eq!(n.as_str(), "1.005");
411 assert_eq!(n.parse::<f64>().unwrap(), 1.005);
412 assert_eq!(n.into_string(), "1.005");
413 }
414
415 #[test]
416 fn known_enum_value_round_trips() {
417 let v: InstType = serde_json::from_str("\"SWAP\"").unwrap();
418 assert_eq!(v, InstType::Swap);
419 assert_eq!(serde_json::to_string(&v).unwrap(), "\"SWAP\"");
420 }
421
422 #[test]
423 fn unknown_enum_value_is_preserved_not_an_error() {
424 let v: InstType = serde_json::from_str("\"FUTURE_THING\"").unwrap();
425 assert_eq!(v, InstType::Unknown("FUTURE_THING".to_owned()));
426 assert_eq!(serde_json::to_string(&v).unwrap(), "\"FUTURE_THING\"");
428 }
429}