1use std::fmt;
5use std::str::FromStr;
6
7use serde::{Deserialize, Serialize};
8
9pub(crate) fn deserialize_vec_or_empty_string<'de, D, T>(
16 deserializer: D,
17) -> Result<Vec<T>, D::Error>
18where
19 D: serde::Deserializer<'de>,
20 T: Deserialize<'de>,
21{
22 #[derive(Deserialize)]
23 #[serde(untagged)]
24 enum WireValue<T> {
25 Sequence(Vec<T>),
26 String(String),
27 Null(()),
28 }
29
30 match WireValue::<T>::deserialize(deserializer)? {
31 WireValue::Sequence(values) => Ok(values),
32 WireValue::String(value) if value.is_empty() => Ok(Vec::new()),
33 WireValue::String(value) => Err(serde::de::Error::custom(format!(
34 "expected an array or empty string, got {value:?}"
35 ))),
36 WireValue::Null(()) => Ok(Vec::new()),
37 }
38}
39
40#[derive(Debug, Clone, Deserialize)]
46pub(crate) struct OkxResponse<D> {
47 pub code: String,
48 pub msg: String,
49 pub data: D,
50}
51
52#[derive(Debug, Clone, Default, PartialEq, Eq, Hash, Serialize, Deserialize)]
69#[serde(transparent)]
70pub struct NumberString(String);
71
72impl NumberString {
73 pub fn as_str(&self) -> &str {
75 &self.0
76 }
77
78 pub fn is_empty(&self) -> bool {
81 self.0.is_empty()
82 }
83
84 pub fn parse<T: FromStr>(&self) -> Result<T, T::Err> {
87 self.0.parse()
88 }
89
90 pub fn into_string(self) -> String {
92 self.0
93 }
94
95 #[cfg(feature = "rust-decimal")]
97 pub fn to_decimal(&self) -> Result<rust_decimal::Decimal, rust_decimal::Error> {
98 self.0.parse()
99 }
100}
101
102impl From<String> for NumberString {
103 fn from(s: String) -> Self {
104 NumberString(s)
105 }
106}
107
108impl From<&str> for NumberString {
109 fn from(s: &str) -> Self {
110 NumberString(s.to_owned())
111 }
112}
113
114impl AsRef<str> for NumberString {
115 fn as_ref(&self) -> &str {
116 &self.0
117 }
118}
119
120impl fmt::Display for NumberString {
121 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
122 f.write_str(&self.0)
123 }
124}
125
126macro_rules! string_enum {
130 (
131 $(#[$meta:meta])*
132 $vis:vis enum $name:ident {
133 $( $(#[$vmeta:meta])* $variant:ident = $wire:literal ),* $(,)?
134 }
135 ) => {
136 $(#[$meta])*
137 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
138 #[non_exhaustive]
139 $vis enum $name {
140 $( $(#[$vmeta])* $variant, )*
141 Unknown(String),
144 }
145
146 impl $name {
147 pub fn as_str(&self) -> &str {
149 match self {
150 $( $name::$variant => $wire, )*
151 $name::Unknown(s) => s.as_str(),
152 }
153 }
154 }
155
156 impl ::core::convert::From<&str> for $name {
157 fn from(s: &str) -> Self {
158 match s {
159 $( $wire => $name::$variant, )*
160 other => $name::Unknown(other.to_owned()),
161 }
162 }
163 }
164
165 impl ::core::fmt::Display for $name {
166 fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
167 f.write_str(self.as_str())
168 }
169 }
170
171 impl ::serde::Serialize for $name {
172 fn serialize<S: ::serde::Serializer>(&self, ser: S) -> ::core::result::Result<S::Ok, S::Error> {
173 ser.serialize_str(self.as_str())
174 }
175 }
176
177 impl<'de> ::serde::Deserialize<'de> for $name {
178 fn deserialize<D: ::serde::Deserializer<'de>>(de: D) -> ::core::result::Result<Self, D::Error> {
179 let s = <::std::string::String as ::serde::Deserialize>::deserialize(de)?;
180 ::core::result::Result::Ok($name::from(s.as_str()))
181 }
182 }
183 };
184}
185
186string_enum! {
187 pub enum InstType {
189 Spot = "SPOT",
191 Margin = "MARGIN",
193 Swap = "SWAP",
195 Futures = "FUTURES",
197 Option = "OPTION",
199 Events = "EVENTS",
201 }
202}
203
204string_enum! {
205 pub enum OrderSide {
207 Buy = "buy",
209 Sell = "sell",
211 }
212}
213
214string_enum! {
215 pub enum OrderType {
217 Market = "market",
219 Limit = "limit",
221 PostOnly = "post_only",
223 Fok = "fok",
225 Ioc = "ioc",
227 OptimalLimitIoc = "optimal_limit_ioc",
229 }
230}
231
232string_enum! {
233 pub enum TradeMode {
235 Cash = "cash",
237 Cross = "cross",
239 Isolated = "isolated",
241 SpotIsolated = "spot_isolated",
243 }
244}
245
246string_enum! {
247 pub enum PositionSide {
249 Long = "long",
251 Short = "short",
253 Net = "net",
255 }
256}
257
258string_enum! {
259 pub enum OrderState {
261 Live = "live",
263 PartiallyFilled = "partially_filled",
265 Filled = "filled",
267 Canceled = "canceled",
269 MmpCanceled = "mmp_canceled",
271 }
272}
273
274#[cfg(test)]
275mod tests {
276 use super::*;
277
278 #[test]
279 fn number_string_parses_and_preserves() {
280 let n = NumberString::from("1.005");
281 assert_eq!(n.as_str(), "1.005");
282 assert_eq!(n.parse::<f64>().unwrap(), 1.005);
283 assert_eq!(n.into_string(), "1.005");
284 }
285
286 #[test]
287 fn known_enum_value_round_trips() {
288 let v: InstType = serde_json::from_str("\"SWAP\"").unwrap();
289 assert_eq!(v, InstType::Swap);
290 assert_eq!(serde_json::to_string(&v).unwrap(), "\"SWAP\"");
291 }
292
293 #[test]
294 fn unknown_enum_value_is_preserved_not_an_error() {
295 let v: InstType = serde_json::from_str("\"FUTURE_THING\"").unwrap();
296 assert_eq!(v, InstType::Unknown("FUTURE_THING".to_owned()));
297 assert_eq!(serde_json::to_string(&v).unwrap(), "\"FUTURE_THING\"");
299 }
300}